Squashed 'vendor/impeccable/' content from commit 00d4856

Amolith created

git-subtree-dir: vendor/impeccable
git-subtree-split: 00d485659af82982aef0328d0419c49a2716d123

Change summary

.agents/skills/adapt/SKILL.md                                                      |  199 
.agents/skills/animate/SKILL.md                                                    |  175 
.agents/skills/audit/SKILL.md                                                      |  148 
.agents/skills/bolder/SKILL.md                                                     |  117 
.agents/skills/clarify/SKILL.md                                                    |  183 
.agents/skills/colorize/SKILL.md                                                   |  143 
.agents/skills/critique/SKILL.md                                                   |  225 
.agents/skills/critique/reference/cognitive-load.md                                |  106 
.agents/skills/critique/reference/heuristics-scoring.md                            |  234 
.agents/skills/critique/reference/personas.md                                      |  178 
.agents/skills/delight/SKILL.md                                                    |  304 
.agents/skills/distill/SKILL.md                                                    |  122 
.agents/skills/harden/SKILL.md                                                     |  389 
.agents/skills/impeccable/SKILL.md                                                 |  365 
.agents/skills/impeccable/reference/color-and-contrast.md                          |  105 
.agents/skills/impeccable/reference/craft.md                                       |   70 
.agents/skills/impeccable/reference/extract.md                                     |   70 
.agents/skills/impeccable/reference/interaction-design.md                          |  195 
.agents/skills/impeccable/reference/motion-design.md                               |   99 
.agents/skills/impeccable/reference/responsive-design.md                           |  114 
.agents/skills/impeccable/reference/spatial-design.md                              |  100 
.agents/skills/impeccable/reference/typography.md                                  |  142 
.agents/skills/impeccable/reference/ux-writing.md                                  |  107 
.agents/skills/impeccable/scripts/cleanup-deprecated.mjs                           |  214 
.agents/skills/layout/SKILL.md                                                     |  125 
.agents/skills/optimize/SKILL.md                                                   |  266 
.agents/skills/overdrive/SKILL.md                                                  |  142 
.agents/skills/polish/SKILL.md                                                     |  224 
.agents/skills/quieter/SKILL.md                                                    |  103 
.agents/skills/shape/SKILL.md                                                      |   96 
.agents/skills/typeset/SKILL.md                                                    |  116 
.claude-plugin/marketplace.json                                                    |   26 
.claude-plugin/plugin.json                                                         |   12 
.claude/agents/anti-patterns.md                                                    |  278 
.claude/skills/adapt/SKILL.md                                                      |  199 
.claude/skills/animate/SKILL.md                                                    |  175 
.claude/skills/audit/SKILL.md                                                      |  148 
.claude/skills/bolder/SKILL.md                                                     |  117 
.claude/skills/clarify/SKILL.md                                                    |  183 
.claude/skills/colorize/SKILL.md                                                   |  143 
.claude/skills/critique/SKILL.md                                                   |  227 
.claude/skills/critique/reference/cognitive-load.md                                |  106 
.claude/skills/critique/reference/heuristics-scoring.md                            |  234 
.claude/skills/critique/reference/personas.md                                      |  178 
.claude/skills/delight/SKILL.md                                                    |  304 
.claude/skills/distill/SKILL.md                                                    |  122 
.claude/skills/harden/SKILL.md                                                     |  389 
.claude/skills/impeccable/SKILL.md                                                 |  365 
.claude/skills/impeccable/reference/color-and-contrast.md                          |  105 
.claude/skills/impeccable/reference/craft.md                                       |   70 
.claude/skills/impeccable/reference/extract.md                                     |   70 
.claude/skills/impeccable/reference/interaction-design.md                          |  195 
.claude/skills/impeccable/reference/motion-design.md                               |   99 
.claude/skills/impeccable/reference/responsive-design.md                           |  114 
.claude/skills/impeccable/reference/spatial-design.md                              |  100 
.claude/skills/impeccable/reference/typography.md                                  |  142 
.claude/skills/impeccable/reference/ux-writing.md                                  |  107 
.claude/skills/impeccable/scripts/cleanup-deprecated.mjs                           |  214 
.claude/skills/layout/SKILL.md                                                     |  125 
.claude/skills/optimize/SKILL.md                                                   |  266 
.claude/skills/overdrive/SKILL.md                                                  |  142 
.claude/skills/polish/SKILL.md                                                     |  224 
.claude/skills/quieter/SKILL.md                                                    |  103 
.claude/skills/shape/SKILL.md                                                      |   96 
.claude/skills/typeset/SKILL.md                                                    |  116 
.codex/skills/adapt/SKILL.md                                                       |  198 
.codex/skills/animate/SKILL.md                                                     |  174 
.codex/skills/audit/SKILL.md                                                       |  147 
.codex/skills/bolder/SKILL.md                                                      |  116 
.codex/skills/clarify/SKILL.md                                                     |  182 
.codex/skills/colorize/SKILL.md                                                    |  142 
.codex/skills/critique/SKILL.md                                                    |  224 
.codex/skills/critique/reference/cognitive-load.md                                 |  106 
.codex/skills/critique/reference/heuristics-scoring.md                             |  234 
.codex/skills/critique/reference/personas.md                                       |  178 
.codex/skills/delight/SKILL.md                                                     |  303 
.codex/skills/distill/SKILL.md                                                     |  121 
.codex/skills/harden/SKILL.md                                                      |  388 
.codex/skills/impeccable/SKILL.md                                                  |  364 
.codex/skills/impeccable/reference/color-and-contrast.md                           |  105 
.codex/skills/impeccable/reference/craft.md                                        |   70 
.codex/skills/impeccable/reference/extract.md                                      |   70 
.codex/skills/impeccable/reference/interaction-design.md                           |  195 
.codex/skills/impeccable/reference/motion-design.md                                |   99 
.codex/skills/impeccable/reference/responsive-design.md                            |  114 
.codex/skills/impeccable/reference/spatial-design.md                               |  100 
.codex/skills/impeccable/reference/typography.md                                   |  142 
.codex/skills/impeccable/reference/ux-writing.md                                   |  107 
.codex/skills/impeccable/scripts/cleanup-deprecated.mjs                            |  214 
.codex/skills/layout/SKILL.md                                                      |  124 
.codex/skills/optimize/SKILL.md                                                    |  265 
.codex/skills/overdrive/SKILL.md                                                   |  141 
.codex/skills/polish/SKILL.md                                                      |  223 
.codex/skills/quieter/SKILL.md                                                     |  102 
.codex/skills/shape/SKILL.md                                                       |   95 
.codex/skills/typeset/SKILL.md                                                     |  115 
.cursor/skills/adapt/SKILL.md                                                      |  197 
.cursor/skills/animate/SKILL.md                                                    |  173 
.cursor/skills/audit/SKILL.md                                                      |  146 
.cursor/skills/bolder/SKILL.md                                                     |  115 
.cursor/skills/clarify/SKILL.md                                                    |  181 
.cursor/skills/colorize/SKILL.md                                                   |  141 
.cursor/skills/critique/SKILL.md                                                   |  223 
.cursor/skills/critique/reference/cognitive-load.md                                |  106 
.cursor/skills/critique/reference/heuristics-scoring.md                            |  234 
.cursor/skills/critique/reference/personas.md                                      |  178 
.cursor/skills/delight/SKILL.md                                                    |  302 
.cursor/skills/distill/SKILL.md                                                    |  120 
.cursor/skills/harden/SKILL.md                                                     |  387 
.cursor/skills/impeccable/SKILL.md                                                 |  363 
.cursor/skills/impeccable/reference/color-and-contrast.md                          |  105 
.cursor/skills/impeccable/reference/craft.md                                       |   70 
.cursor/skills/impeccable/reference/extract.md                                     |   70 
.cursor/skills/impeccable/reference/interaction-design.md                          |  195 
.cursor/skills/impeccable/reference/motion-design.md                               |   99 
.cursor/skills/impeccable/reference/responsive-design.md                           |  114 
.cursor/skills/impeccable/reference/spatial-design.md                              |  100 
.cursor/skills/impeccable/reference/typography.md                                  |  142 
.cursor/skills/impeccable/reference/ux-writing.md                                  |  107 
.cursor/skills/impeccable/scripts/cleanup-deprecated.mjs                           |  214 
.cursor/skills/layout/SKILL.md                                                     |  123 
.cursor/skills/optimize/SKILL.md                                                   |  264 
.cursor/skills/overdrive/SKILL.md                                                  |  140 
.cursor/skills/polish/SKILL.md                                                     |  222 
.cursor/skills/quieter/SKILL.md                                                    |  101 
.cursor/skills/shape/SKILL.md                                                      |   94 
.cursor/skills/typeset/SKILL.md                                                    |  114 
.gemini/skills/adapt/SKILL.md                                                      |  197 
.gemini/skills/animate/SKILL.md                                                    |  173 
.gemini/skills/audit/SKILL.md                                                      |  146 
.gemini/skills/bolder/SKILL.md                                                     |  115 
.gemini/skills/clarify/SKILL.md                                                    |  181 
.gemini/skills/colorize/SKILL.md                                                   |  141 
.gemini/skills/critique/SKILL.md                                                   |  223 
.gemini/skills/critique/reference/cognitive-load.md                                |  106 
.gemini/skills/critique/reference/heuristics-scoring.md                            |  234 
.gemini/skills/critique/reference/personas.md                                      |  178 
.gemini/skills/delight/SKILL.md                                                    |  302 
.gemini/skills/distill/SKILL.md                                                    |  120 
.gemini/skills/harden/SKILL.md                                                     |  387 
.gemini/skills/impeccable/SKILL.md                                                 |  362 
.gemini/skills/impeccable/reference/color-and-contrast.md                          |  105 
.gemini/skills/impeccable/reference/craft.md                                       |   70 
.gemini/skills/impeccable/reference/extract.md                                     |   70 
.gemini/skills/impeccable/reference/interaction-design.md                          |  195 
.gemini/skills/impeccable/reference/motion-design.md                               |   99 
.gemini/skills/impeccable/reference/responsive-design.md                           |  114 
.gemini/skills/impeccable/reference/spatial-design.md                              |  100 
.gemini/skills/impeccable/reference/typography.md                                  |  142 
.gemini/skills/impeccable/reference/ux-writing.md                                  |  107 
.gemini/skills/impeccable/scripts/cleanup-deprecated.mjs                           |  214 
.gemini/skills/layout/SKILL.md                                                     |  123 
.gemini/skills/optimize/SKILL.md                                                   |  264 
.gemini/skills/overdrive/SKILL.md                                                  |  140 
.gemini/skills/polish/SKILL.md                                                     |  222 
.gemini/skills/quieter/SKILL.md                                                    |  101 
.gemini/skills/shape/SKILL.md                                                      |   94 
.gemini/skills/typeset/SKILL.md                                                    |  114 
.github/CODEOWNERS                                                                 |    8 
.github/ISSUE_TEMPLATE/bug_report.md                                               |   31 
.github/ISSUE_TEMPLATE/feature_request.md                                          |   30 
.github/PULL_REQUEST_TEMPLATE.md                                                   |   21 
.github/workflows/ci.yml                                                           |   41 
.gitignore                                                                         |   40 
.kiro/skills/adapt/SKILL.md                                                        |  197 
.kiro/skills/animate/SKILL.md                                                      |  173 
.kiro/skills/audit/SKILL.md                                                        |  146 
.kiro/skills/bolder/SKILL.md                                                       |  115 
.kiro/skills/clarify/SKILL.md                                                      |  181 
.kiro/skills/colorize/SKILL.md                                                     |  141 
.kiro/skills/critique/SKILL.md                                                     |  223 
.kiro/skills/critique/reference/cognitive-load.md                                  |  106 
.kiro/skills/critique/reference/heuristics-scoring.md                              |  234 
.kiro/skills/critique/reference/personas.md                                        |  178 
.kiro/skills/delight/SKILL.md                                                      |  302 
.kiro/skills/distill/SKILL.md                                                      |  120 
.kiro/skills/harden/SKILL.md                                                       |  387 
.kiro/skills/impeccable/SKILL.md                                                   |  363 
.kiro/skills/impeccable/reference/color-and-contrast.md                            |  105 
.kiro/skills/impeccable/reference/craft.md                                         |   70 
.kiro/skills/impeccable/reference/extract.md                                       |   70 
.kiro/skills/impeccable/reference/interaction-design.md                            |  195 
.kiro/skills/impeccable/reference/motion-design.md                                 |   99 
.kiro/skills/impeccable/reference/responsive-design.md                             |  114 
.kiro/skills/impeccable/reference/spatial-design.md                                |  100 
.kiro/skills/impeccable/reference/typography.md                                    |  142 
.kiro/skills/impeccable/reference/ux-writing.md                                    |  107 
.kiro/skills/impeccable/scripts/cleanup-deprecated.mjs                             |  214 
.kiro/skills/layout/SKILL.md                                                       |  123 
.kiro/skills/optimize/SKILL.md                                                     |  264 
.kiro/skills/overdrive/SKILL.md                                                    |  140 
.kiro/skills/polish/SKILL.md                                                       |  222 
.kiro/skills/quieter/SKILL.md                                                      |  101 
.kiro/skills/shape/SKILL.md                                                        |   94 
.kiro/skills/typeset/SKILL.md                                                      |  114 
.opencode/skills/adapt/SKILL.md                                                    |  199 
.opencode/skills/animate/SKILL.md                                                  |  175 
.opencode/skills/audit/SKILL.md                                                    |  148 
.opencode/skills/bolder/SKILL.md                                                   |  117 
.opencode/skills/clarify/SKILL.md                                                  |  183 
.opencode/skills/colorize/SKILL.md                                                 |  143 
.opencode/skills/critique/SKILL.md                                                 |  227 
.opencode/skills/critique/reference/cognitive-load.md                              |  106 
.opencode/skills/critique/reference/heuristics-scoring.md                          |  234 
.opencode/skills/critique/reference/personas.md                                    |  178 
.opencode/skills/delight/SKILL.md                                                  |  304 
.opencode/skills/distill/SKILL.md                                                  |  122 
.opencode/skills/harden/SKILL.md                                                   |  389 
.opencode/skills/impeccable/SKILL.md                                               |  365 
.opencode/skills/impeccable/reference/color-and-contrast.md                        |  105 
.opencode/skills/impeccable/reference/craft.md                                     |   70 
.opencode/skills/impeccable/reference/extract.md                                   |   70 
.opencode/skills/impeccable/reference/interaction-design.md                        |  195 
.opencode/skills/impeccable/reference/motion-design.md                             |   99 
.opencode/skills/impeccable/reference/responsive-design.md                         |  114 
.opencode/skills/impeccable/reference/spatial-design.md                            |  100 
.opencode/skills/impeccable/reference/typography.md                                |  142 
.opencode/skills/impeccable/reference/ux-writing.md                                |  107 
.opencode/skills/impeccable/scripts/cleanup-deprecated.mjs                         |  214 
.opencode/skills/layout/SKILL.md                                                   |  125 
.opencode/skills/optimize/SKILL.md                                                 |  266 
.opencode/skills/overdrive/SKILL.md                                                |  142 
.opencode/skills/polish/SKILL.md                                                   |  224 
.opencode/skills/quieter/SKILL.md                                                  |  103 
.opencode/skills/shape/SKILL.md                                                    |   96 
.opencode/skills/typeset/SKILL.md                                                  |  116 
.pi/skills/adapt/SKILL.md                                                          |  197 
.pi/skills/animate/SKILL.md                                                        |  173 
.pi/skills/audit/SKILL.md                                                          |  146 
.pi/skills/bolder/SKILL.md                                                         |  115 
.pi/skills/clarify/SKILL.md                                                        |  181 
.pi/skills/colorize/SKILL.md                                                       |  141 
.pi/skills/critique/SKILL.md                                                       |  225 
.pi/skills/critique/reference/cognitive-load.md                                    |  106 
.pi/skills/critique/reference/heuristics-scoring.md                                |  234 
.pi/skills/critique/reference/personas.md                                          |  178 
.pi/skills/delight/SKILL.md                                                        |  302 
.pi/skills/distill/SKILL.md                                                        |  120 
.pi/skills/harden/SKILL.md                                                         |  387 
.pi/skills/impeccable/SKILL.md                                                     |  363 
.pi/skills/impeccable/reference/color-and-contrast.md                              |  105 
.pi/skills/impeccable/reference/craft.md                                           |   70 
.pi/skills/impeccable/reference/extract.md                                         |   70 
.pi/skills/impeccable/reference/interaction-design.md                              |  195 
.pi/skills/impeccable/reference/motion-design.md                                   |   99 
.pi/skills/impeccable/reference/responsive-design.md                               |  114 
.pi/skills/impeccable/reference/spatial-design.md                                  |  100 
.pi/skills/impeccable/reference/typography.md                                      |  142 
.pi/skills/impeccable/reference/ux-writing.md                                      |  107 
.pi/skills/impeccable/scripts/cleanup-deprecated.mjs                               |  214 
.pi/skills/layout/SKILL.md                                                         |  123 
.pi/skills/optimize/SKILL.md                                                       |  264 
.pi/skills/overdrive/SKILL.md                                                      |  140 
.pi/skills/polish/SKILL.md                                                         |  222 
.pi/skills/quieter/SKILL.md                                                        |  101 
.pi/skills/shape/SKILL.md                                                          |   94 
.pi/skills/typeset/SKILL.md                                                        |  114 
.rovodev/skills/adapt/SKILL.md                                                     |  199 
.rovodev/skills/animate/SKILL.md                                                   |  175 
.rovodev/skills/audit/SKILL.md                                                     |  148 
.rovodev/skills/bolder/SKILL.md                                                    |  117 
.rovodev/skills/clarify/SKILL.md                                                   |  183 
.rovodev/skills/colorize/SKILL.md                                                  |  143 
.rovodev/skills/critique/SKILL.md                                                  |  227 
.rovodev/skills/critique/reference/cognitive-load.md                               |  106 
.rovodev/skills/critique/reference/heuristics-scoring.md                           |  234 
.rovodev/skills/critique/reference/personas.md                                     |  178 
.rovodev/skills/delight/SKILL.md                                                   |  304 
.rovodev/skills/distill/SKILL.md                                                   |  122 
.rovodev/skills/harden/SKILL.md                                                    |  389 
.rovodev/skills/impeccable/SKILL.md                                                |  365 
.rovodev/skills/impeccable/reference/color-and-contrast.md                         |  105 
.rovodev/skills/impeccable/reference/craft.md                                      |   70 
.rovodev/skills/impeccable/reference/extract.md                                    |   70 
.rovodev/skills/impeccable/reference/interaction-design.md                         |  195 
.rovodev/skills/impeccable/reference/motion-design.md                              |   99 
.rovodev/skills/impeccable/reference/responsive-design.md                          |  114 
.rovodev/skills/impeccable/reference/spatial-design.md                             |  100 
.rovodev/skills/impeccable/reference/typography.md                                 |  142 
.rovodev/skills/impeccable/reference/ux-writing.md                                 |  107 
.rovodev/skills/impeccable/scripts/cleanup-deprecated.mjs                          |  214 
.rovodev/skills/layout/SKILL.md                                                    |  125 
.rovodev/skills/optimize/SKILL.md                                                  |  266 
.rovodev/skills/overdrive/SKILL.md                                                 |  142 
.rovodev/skills/polish/SKILL.md                                                    |  224 
.rovodev/skills/quieter/SKILL.md                                                   |  103 
.rovodev/skills/shape/SKILL.md                                                     |   96 
.rovodev/skills/typeset/SKILL.md                                                   |  116 
.trae-cn/skills/adapt/SKILL.md                                                     |  199 
.trae-cn/skills/animate/SKILL.md                                                   |  175 
.trae-cn/skills/audit/SKILL.md                                                     |  148 
.trae-cn/skills/bolder/SKILL.md                                                    |  117 
.trae-cn/skills/clarify/SKILL.md                                                   |  183 
.trae-cn/skills/colorize/SKILL.md                                                  |  143 
.trae-cn/skills/critique/SKILL.md                                                  |  225 
.trae-cn/skills/critique/reference/cognitive-load.md                               |  106 
.trae-cn/skills/critique/reference/heuristics-scoring.md                           |  234 
.trae-cn/skills/critique/reference/personas.md                                     |  178 
.trae-cn/skills/delight/SKILL.md                                                   |  304 
.trae-cn/skills/distill/SKILL.md                                                   |  122 
.trae-cn/skills/harden/SKILL.md                                                    |  389 
.trae-cn/skills/impeccable/SKILL.md                                                |  365 
.trae-cn/skills/impeccable/reference/color-and-contrast.md                         |  105 
.trae-cn/skills/impeccable/reference/craft.md                                      |   70 
.trae-cn/skills/impeccable/reference/extract.md                                    |   70 
.trae-cn/skills/impeccable/reference/interaction-design.md                         |  195 
.trae-cn/skills/impeccable/reference/motion-design.md                              |   99 
.trae-cn/skills/impeccable/reference/responsive-design.md                          |  114 
.trae-cn/skills/impeccable/reference/spatial-design.md                             |  100 
.trae-cn/skills/impeccable/reference/typography.md                                 |  142 
.trae-cn/skills/impeccable/reference/ux-writing.md                                 |  107 
.trae-cn/skills/impeccable/scripts/cleanup-deprecated.mjs                          |  214 
.trae-cn/skills/layout/SKILL.md                                                    |  125 
.trae-cn/skills/optimize/SKILL.md                                                  |  266 
.trae-cn/skills/overdrive/SKILL.md                                                 |  142 
.trae-cn/skills/polish/SKILL.md                                                    |  224 
.trae-cn/skills/quieter/SKILL.md                                                   |  103 
.trae-cn/skills/shape/SKILL.md                                                     |   96 
.trae-cn/skills/typeset/SKILL.md                                                   |  116 
.trae/skills/adapt/SKILL.md                                                        |  199 
.trae/skills/animate/SKILL.md                                                      |  175 
.trae/skills/audit/SKILL.md                                                        |  148 
.trae/skills/bolder/SKILL.md                                                       |  117 
.trae/skills/clarify/SKILL.md                                                      |  183 
.trae/skills/colorize/SKILL.md                                                     |  143 
.trae/skills/critique/SKILL.md                                                     |  225 
.trae/skills/critique/reference/cognitive-load.md                                  |  106 
.trae/skills/critique/reference/heuristics-scoring.md                              |  234 
.trae/skills/critique/reference/personas.md                                        |  178 
.trae/skills/delight/SKILL.md                                                      |  304 
.trae/skills/distill/SKILL.md                                                      |  122 
.trae/skills/harden/SKILL.md                                                       |  389 
.trae/skills/impeccable/SKILL.md                                                   |  365 
.trae/skills/impeccable/reference/color-and-contrast.md                            |  105 
.trae/skills/impeccable/reference/craft.md                                         |   70 
.trae/skills/impeccable/reference/extract.md                                       |   70 
.trae/skills/impeccable/reference/interaction-design.md                            |  195 
.trae/skills/impeccable/reference/motion-design.md                                 |   99 
.trae/skills/impeccable/reference/responsive-design.md                             |  114 
.trae/skills/impeccable/reference/spatial-design.md                                |  100 
.trae/skills/impeccable/reference/typography.md                                    |  142 
.trae/skills/impeccable/reference/ux-writing.md                                    |  107 
.trae/skills/impeccable/scripts/cleanup-deprecated.mjs                             |  214 
.trae/skills/layout/SKILL.md                                                       |  125 
.trae/skills/optimize/SKILL.md                                                     |  266 
.trae/skills/overdrive/SKILL.md                                                    |  142 
.trae/skills/polish/SKILL.md                                                       |  224 
.trae/skills/quieter/SKILL.md                                                      |  103 
.trae/skills/shape/SKILL.md                                                        |   96 
.trae/skills/typeset/SKILL.md                                                      |  116 
AGENTS.md                                                                          |  250 
CLAUDE.md                                                                          |  151 
DEVELOP.md                                                                         |  206 
HARNESSES.md                                                                       |   86 
LICENSE                                                                            |  191 
NOTICE.md                                                                          |   17 
README.md                                                                          |  246 
README.npm.md                                                                      |   76 
bin/cli.js                                                                         |   64 
bin/commands/skills.mjs                                                            |  648 
biome.json                                                                         |   12 
bun.lock                                                                           |  298 
content/site/anti-patterns-catalog.js                                              |  353 
content/site/partials/header.html                                                  |   20 
content/site/skills/adapt.md                                                       |   40 
content/site/skills/animate.md                                                     |   42 
content/site/skills/arrange.md                                                     |   41 
content/site/skills/audit.md                                                       |   50 
content/site/skills/bolder.md                                                      |   40 
content/site/skills/clarify.md                                                     |   42 
content/site/skills/colorize.md                                                    |   38 
content/site/skills/critique.md                                                    |   43 
content/site/skills/delight.md                                                     |   42 
content/site/skills/distill.md                                                     |   44 
content/site/skills/extract.md                                                     |   43 
content/site/skills/harden.md                                                      |   45 
content/site/skills/impeccable.md                                                  |   51 
content/site/skills/layout.md                                                      |   41 
content/site/skills/normalize.md                                                   |   41 
content/site/skills/onboard.md                                                     |   40 
content/site/skills/optimize.md                                                    |   56 
content/site/skills/overdrive.md                                                   |   30 
content/site/skills/polish.md                                                      |   46 
content/site/skills/quieter.md                                                     |   40 
content/site/skills/shape.md                                                       |   40 
content/site/skills/typeset.md                                                     |   42 
content/site/tutorials/critique-with-overlay.md                                    |  129 
content/site/tutorials/getting-started.md                                          |   82 
extension/STORE_LISTING.md                                                         |   63 
extension/background/service-worker.js                                             |  263 
extension/content/content-script.js                                                |  125 
extension/devtools/devtools.html                                                   |    5 
extension/devtools/devtools.js                                                     |   50 
extension/devtools/panel.css                                                       |  520 
extension/devtools/panel.html                                                      |   66 
extension/devtools/panel.js                                                        |  519 
extension/devtools/sidebar.css                                                     |   97 
extension/devtools/sidebar.html                                                    |   13 
extension/devtools/sidebar.js                                                      |  103 
extension/icons/icon-128.png                                                       |    0 
extension/icons/icon-16.png                                                        |    0 
extension/icons/icon-32.png                                                        |    0 
extension/icons/icon-48.png                                                        |    0 
extension/icons/icon.svg                                                           |    4 
extension/icons/promo-small.png                                                    |    0 
extension/manifest.json                                                            |   32 
extension/popup/popup.css                                                          |  110 
extension/popup/popup.html                                                         |   29 
extension/popup/popup.js                                                           |   67 
functions/api/download/[type]/[provider]/[id].js                                   |   46 
functions/api/download/bundle/[provider].js                                        |   29 
lib/download-providers.js                                                          |   24 
package.json                                                                       |   71 
public/antipattern-examples/bad-contrast.html                                      |  207 
public/antipattern-examples/cardocalypse.html                                      |  375 
public/antipattern-examples/inter-everywhere.html                                  |  317 
public/antipattern-examples/layout-templates.html                                  |  322 
public/antipattern-examples/lazy-cool.html                                         |  320 
public/antipattern-examples/lazy-impact.html                                       |  378 
public/antipattern-examples/massive-icons.html                                     |  199 
public/antipattern-examples/modal-abuse.html                                       |  436 
public/antipattern-examples/purple-gradients.html                                  |  241 
public/antipattern-examples/redundant-ux-writing.html                              |  207 
public/antipattern-examples/thick-border-cards.html                                |  205 
public/antipattern-examples/visual-mode-demo.html                                  |  231 
public/antipattern-images/bad-contrast.png                                         |    0 
public/antipattern-images/cardocalypse.png                                         |    0 
public/antipattern-images/inter-everywhere.png                                     |    0 
public/antipattern-images/layout-templates.png                                     |    0 
public/antipattern-images/lazy-cool.png                                            |    0 
public/antipattern-images/lazy-impact.png                                          |    0 
public/antipattern-images/massive-icons.png                                        |    0 
public/antipattern-images/modal-abuse.png                                          |    0 
public/antipattern-images/purple-gradients.png                                     |    0 
public/antipattern-images/redundant-ux-writing.png                                 |    0 
public/antipattern-images/thick-border-cards.png                                   |    0 
public/app.js                                                                      |  226 
public/apple-touch-icon.png                                                        |    0 
public/assets/antigravity-logo.png                                                 |    0 
public/assets/claude-logo.png                                                      |    0 
public/assets/cursor-logo.png                                                      |    0 
public/assets/dashboard.after.webp                                                 |    0 
public/assets/dashboard.before.webp                                                |    0 
public/assets/diamond.svg                                                          |    3 
public/assets/extension-detection.png                                              |    0 
public/assets/form.after.webp                                                      |    0 
public/assets/form.before.webp                                                     |    0 
public/assets/gemini-logo.png                                                      |    0 
public/assets/github-logo.png                                                      |    0 
public/assets/kiro-logo.png                                                        |    0 
public/assets/landing.after.webp                                                   |    0 
public/assets/landing.before.webp                                                  |    0 
public/assets/newsletter-icon.png                                                  |    0 
public/assets/newsletter-icon.svg                                                  |   18 
public/assets/openai-logo.png                                                      |    0 
public/assets/opencode-logo.png                                                    |    0 
public/assets/pi-logo.svg                                                          |   22 
public/cheatsheet.html                                                             |  316 
public/css/gallery.css                                                             |  184 
public/css/main.css                                                                | 3758 
public/css/problem-section.css                                                     |  121 
public/css/skill-demos.css                                                         |  962 
public/css/sub-pages.css                                                           | 2070 
public/css/tokens.css                                                              |  102 
public/css/workflow.css                                                            | 1347 
public/favicon.svg                                                                 |    4 
public/index.html                                                                  |  734 
public/js/components/art-gallery.js                                                |  142 
public/js/components/foundation-animations.js                                      |   53 
public/js/components/foundation-grid.js                                            |   26 
public/js/components/framework-viz.js                                              |  366 
public/js/components/glass-terminal.js                                             |  643 
public/js/components/lens.js                                                       |   13 
public/js/components/section-nav.js                                                |   80 
public/js/data.js                                                                  |  140 
public/js/demo-renderer.js                                                         |  212 
public/js/demo-toggles.js                                                          |  160 
public/js/demos/commands/adapt.js                                                  |   80 
public/js/demos/commands/animate.js                                                |   44 
public/js/demos/commands/arrange.js                                                |   47 
public/js/demos/commands/audit.js                                                  |   40 
public/js/demos/commands/bolder.js                                                 |   24 
public/js/demos/commands/clarify.js                                                |   36 
public/js/demos/commands/colorize.js                                               |   56 
public/js/demos/commands/critique.js                                               |   47 
public/js/demos/commands/delight.js                                                |   53 
public/js/demos/commands/distill.js                                                |   47 
public/js/demos/commands/extract.js                                                |   62 
public/js/demos/commands/harden.js                                                 |   42 
public/js/demos/commands/index.js                                                  |   41 
public/js/demos/commands/layout.js                                                 |   47 
public/js/demos/commands/normalize.js                                              |   42 
public/js/demos/commands/onboard.js                                                |   31 
public/js/demos/commands/optimize.js                                               |   57 
public/js/demos/commands/overdrive.js                                              |  342 
public/js/demos/commands/polish.js                                                 |   33 
public/js/demos/commands/quieter.js                                                |   28 
public/js/demos/commands/typeset.js                                                |   23 
public/js/demos/index.js                                                           |    6 
public/js/demos/skills/color-and-contrast.js                                       |   68 
public/js/demos/skills/index.js                                                    |   26 
public/js/demos/skills/interaction-design.js                                       |   79 
public/js/demos/skills/motion-design.js                                            |   69 
public/js/demos/skills/responsive-design.js                                        |   91 
public/js/demos/skills/spatial-design.js                                           |   62 
public/js/demos/skills/typography.js                                               |   38 
public/js/demos/skills/ux-writing.js                                               |   71 
public/js/effects/hero-shader.js                                                   |   57 
public/js/effects/liquid-canvas.js                                                 |  165 
public/js/effects/split-compare.js                                                 |  229 
public/js/generated/counts.js                                                      |    3 
public/js/utils/reveal.js                                                          |   18 
public/js/utils/scroll.js                                                          |   99 
public/js/utils/theme.js                                                           |   56 
public/og-image.jpg                                                                |    0 
public/privacy.html                                                                |   81 
public/robots.txt                                                                  |    6 
public/sitemap.xml                                                                 |   69 
scripts/build-browser-detector.js                                                  |   47 
scripts/build-extension.js                                                         |   85 
scripts/build-sub-pages.js                                                         |  735 
scripts/build.js                                                                   |  664 
scripts/generate-extension-icons.js                                                |   38 
scripts/generate-og-image.js                                                       |  256 
scripts/generate-promo-tile.js                                                     |  145 
scripts/lib/render-markdown.js                                                     |  156 
scripts/lib/render-page.js                                                         |  247 
scripts/lib/sub-pages-data.js                                                      |  248 
scripts/lib/transformers/factory.js                                                |  132 
scripts/lib/transformers/index.js                                                  |   14 
scripts/lib/transformers/providers.js                                              |   79 
scripts/lib/transformers/shared.js                                                 |   81 
scripts/lib/utils.js                                                               |  487 
scripts/lib/zip.js                                                                 |   62 
scripts/screenshot-antipatterns.js                                                 |  102 
server/index.js                                                                    |  210 
server/lib/api-handlers.js                                                         |  165 
server/lib/validation.js                                                           |   40 
skills-lock.json                                                                   |  110 
source/skills/adapt/SKILL.md                                                       |  199 
source/skills/animate/SKILL.md                                                     |  174 
source/skills/audit/SKILL.md                                                       |  147 
source/skills/bolder/SKILL.md                                                      |  116 
source/skills/clarify/SKILL.md                                                     |  183 
source/skills/colorize/SKILL.md                                                    |  142 
source/skills/critique/SKILL.md                                                    |  226 
source/skills/critique/reference/cognitive-load.md                                 |  106 
source/skills/critique/reference/heuristics-scoring.md                             |  234 
source/skills/critique/reference/personas.md                                       |  178 
source/skills/delight/SKILL.md                                                     |  303 
source/skills/distill/SKILL.md                                                     |  121 
source/skills/harden/SKILL.md                                                      |  389 
source/skills/impeccable/SKILL.md                                                  |  364 
source/skills/impeccable/reference/color-and-contrast.md                           |  105 
source/skills/impeccable/reference/craft.md                                        |   70 
source/skills/impeccable/reference/extract.md                                      |   70 
source/skills/impeccable/reference/interaction-design.md                           |  195 
source/skills/impeccable/reference/motion-design.md                                |   99 
source/skills/impeccable/reference/responsive-design.md                            |  114 
source/skills/impeccable/reference/spatial-design.md                               |  100 
source/skills/impeccable/reference/typography.md                                   |  142 
source/skills/impeccable/reference/ux-writing.md                                   |  107 
source/skills/impeccable/scripts/cleanup-deprecated.mjs                            |  214 
source/skills/layout/SKILL.md                                                      |  124 
source/skills/optimize/SKILL.md                                                    |  266 
source/skills/overdrive/SKILL.md                                                   |  141 
source/skills/polish/SKILL.md                                                      |  224 
source/skills/quieter/SKILL.md                                                     |  102 
source/skills/shape/SKILL.md                                                       |   95 
source/skills/typeset/SKILL.md                                                     |  115 
src/detect-antipatterns-browser.js                                                 | 2402 
src/detect-antipatterns.mjs                                                        | 3590 
tests/build.test.js                                                                |  297 
tests/cleanup-deprecated.test.mjs                                                  |  236 
tests/detect-antipatterns-browser.test.mjs                                         |   99 
tests/detect-antipatterns-fixtures.test.mjs                                        |  266 
tests/detect-antipatterns.test.js                                                  | 1231 
tests/fixtures/antipatterns/color.html                                             |  134 
tests/fixtures/antipatterns/cramped-padding.html                                   |  214 
tests/fixtures/antipatterns/cssinjs-should-flag.tsx                                |   44 
tests/fixtures/antipatterns/cssinjs-should-pass.tsx                                |   25 
tests/fixtures/antipatterns/external-styles.css                                    |   33 
tests/fixtures/antipatterns/framework-next-cssinjs/components/FeatureGrid.tsx      |   58 
tests/fixtures/antipatterns/framework-next-cssinjs/components/GlobalStyle.tsx      |   18 
tests/fixtures/antipatterns/framework-next-cssinjs/components/Hero.tsx             |   61 
tests/fixtures/antipatterns/framework-next-cssinjs/components/Testimonials.tsx     |   53 
tests/fixtures/antipatterns/framework-next-cssinjs/next.config.js                  |    8 
tests/fixtures/antipatterns/framework-next-cssinjs/pages/_app.tsx                  |   27 
tests/fixtures/antipatterns/framework-next-cssinjs/pages/index.tsx                 |   13 
tests/fixtures/antipatterns/framework-next-modules/app/globals.css                 |   18 
tests/fixtures/antipatterns/framework-next-modules/app/layout.tsx                  |   19 
tests/fixtures/antipatterns/framework-next-modules/app/page.module.css             |   25 
tests/fixtures/antipatterns/framework-next-modules/app/page.tsx                    |   19 
tests/fixtures/antipatterns/framework-next-modules/components/Sidebar.module.css   |   38 
tests/fixtures/antipatterns/framework-next-modules/components/Sidebar.tsx          |   24 
tests/fixtures/antipatterns/framework-next-modules/components/StatsCard.module.css |   34 
tests/fixtures/antipatterns/framework-next-modules/components/StatsCard.tsx        |   21 
tests/fixtures/antipatterns/framework-next-modules/next.config.mjs                 |    4 
tests/fixtures/antipatterns/framework-next-tailwind/app/globals.css                |   20 
tests/fixtures/antipatterns/framework-next-tailwind/app/layout.tsx                 |   22 
tests/fixtures/antipatterns/framework-next-tailwind/app/page.tsx                   |   50 
tests/fixtures/antipatterns/framework-next-tailwind/components/FeatureCard.tsx     |   15 
tests/fixtures/antipatterns/framework-next-tailwind/components/PricingCard.tsx     |   35 
tests/fixtures/antipatterns/framework-next-tailwind/next.config.mjs                |    4 
tests/fixtures/antipatterns/framework-next-tailwind/tailwind.config.ts             |   20 
tests/fixtures/antipatterns/framework-vite/main.tsx                                |    9 
tests/fixtures/antipatterns/framework-vite/vite.config.ts                          |    9 
tests/fixtures/antipatterns/glow.html                                              |  164 
tests/fixtures/antipatterns/icon-tile-stack.html                                   |  247 
tests/fixtures/antipatterns/jsx-should-flag.jsx                                    |   56 
tests/fixtures/antipatterns/jsx-should-pass.jsx                                    |   33 
tests/fixtures/antipatterns/layout.html                                            |  138 
tests/fixtures/antipatterns/legitimate-borders.html                                |  113 
tests/fixtures/antipatterns/linked-stylesheet.html                                 |   48 
tests/fixtures/antipatterns/modern-color-borders.html                              |  326 
tests/fixtures/antipatterns/motion.html                                            |  160 
tests/fixtures/antipatterns/multifile/App.tsx                                      |   14 
tests/fixtures/antipatterns/multifile/Card.tsx                                     |   16 
tests/fixtures/antipatterns/multifile/styles.css                                   |    9 
tests/fixtures/antipatterns/multifile/theme.scss                                   |    6 
tests/fixtures/antipatterns/multifile/variables.scss                               |    3 
tests/fixtures/antipatterns/overlay-positioning.html                               |  525 
tests/fixtures/antipatterns/partial-component.html                                 |   11 
tests/fixtures/antipatterns/quality.html                                           |  227 
tests/fixtures/antipatterns/should-flag.html                                       |  136 
tests/fixtures/antipatterns/should-pass.html                                       |   84 
tests/fixtures/antipatterns/svelte-should-flag.svelte                              |   33 
tests/fixtures/antipatterns/svelte-should-pass.svelte                              |   20 
tests/fixtures/antipatterns/typography-should-flag.html                            |   39 
tests/fixtures/antipatterns/typography-should-pass.html                            |   45 
tests/fixtures/antipatterns/vue-should-flag.vue                                    |   36 
tests/fixtures/antipatterns/vue-should-pass.vue                                    |   23 
tests/lib/transformers/factory.test.js                                             |  276 
tests/lib/transformers/providers.test.js                                           |  192 
tests/lib/utils.test.js                                                            |  708 
tests/server/download-validation.test.js                                           |   46 
tests/skills-cli.test.js                                                           |  417 
wrangler.toml                                                                      |    3 
638 files changed, 103,542 insertions(+)

Detailed changes

.agents/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,199 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target] [context (mobile, tablet, print...)]"
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.agents/skills/animate/SKILL.md 🔗

@@ -0,0 +1,175 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.agents/skills/audit/SKILL.md 🔗

@@ -0,0 +1,148 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.agents/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,117 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.agents/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,183 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.agents/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,143 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.agents/skills/critique/SKILL.md 🔗

@@ -0,0 +1,225 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `.github/copilot-instructions.md` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. ask the user directly to clarify what you cannot infer. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.agents/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.agents/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.agents/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `.github/copilot-instructions.md` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.agents/skills/delight/SKILL.md 🔗

@@ -0,0 +1,304 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.agents/skills/distill/SKILL.md 🔗

@@ -0,0 +1,122 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.agents/skills/harden/SKILL.md 🔗

@@ -0,0 +1,389 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.agents/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,365 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[craft|teach|extract]"
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .agents/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: the model is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+ask the user directly to clarify what you cannot infer. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then ask the user directly to clarify what you cannot infer. whether they'd also like the Design Context appended to .github/copilot-instructions.md. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.agents/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.agents/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.agents/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, ask the user directly to clarify what you cannot infer. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.agents/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.agents/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.agents/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.agents/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.agents/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.agents/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.agents/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.agents/skills/layout/SKILL.md 🔗

@@ -0,0 +1,125 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.agents/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,266 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.agents/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,142 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **ask the user directly to clarify what you cannot infer.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.agents/skills/polish/SKILL.md 🔗

@@ -0,0 +1,224 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.agents/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,103 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.agents/skills/shape/SKILL.md 🔗

@@ -0,0 +1,96 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[feature to shape]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+ask the user directly to clarify what you cannot infer. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.agents/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,116 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.claude-plugin/marketplace.json 🔗

@@ -0,0 +1,26 @@
+{
+  "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
+  "name": "impeccable",
+  "metadata": {
+    "description": "Design fluency for AI harnesses. 1 skill, 18 commands, and curated anti-patterns for impeccable frontend design."
+  },
+  "owner": {
+    "name": "Paul Bakaus",
+    "email": "paul@paulbakaus.com"
+  },
+  "plugins": [
+    {
+      "name": "impeccable",
+      "description": "Design vocabulary and skills for frontend development. Includes 18 commands (/polish, /distill, /audit, /typeset, /overdrive, etc.) and an enhanced impeccable skill with curated anti-patterns.",
+      "version": "2.1.1",
+      "author": {
+        "name": "Paul Bakaus",
+        "email": "paul@paulbakaus.com"
+      },
+      "source": "./",
+      "category": "design",
+      "homepage": "https://impeccable.style",
+      "tags": ["design", "frontend", "ui", "ux", "skills", "commands"]
+    }
+  ]
+}

.claude-plugin/plugin.json 🔗

@@ -0,0 +1,12 @@
+{
+  "name": "impeccable",
+  "description": "Design vocabulary and skills for frontend development. Includes 18 commands (/polish, /distill, /audit, /typeset, /overdrive, etc.) and an enhanced impeccable skill with curated anti-patterns.",
+  "version": "2.1.1",
+  "author": {
+    "name": "Paul Bakaus",
+    "email": "paul@paulbakaus.com"
+  },
+  "homepage": "https://impeccable.style",
+  "repository": "https://github.com/pbakaus/impeccable",
+  "skills": "./.claude/skills"
+}

.claude/agents/anti-patterns.md 🔗

@@ -0,0 +1,278 @@
+---
+name: anti-patterns
+description: Use when adding, modifying, or debugging an anti-pattern detection rule in this repo. Walks through the TDD recipe, the rule schema, all five plug-in points, jsdom constraints, the cross-validation step against the impeccable skill, and the post-implementation checklist. Trigger this for any work touching src/detect-antipatterns.mjs, tests/fixtures/antipatterns/, or extension/detector/.
+tools: Read, Edit, Write, Glob, Grep, Bash, mcp__claude-in-chrome__navigate, mcp__claude-in-chrome__javascript_tool, mcp__claude-in-chrome__tabs_context_mcp, mcp__claude-in-chrome__tabs_create_mcp
+---
+
+# Anti-Pattern Engine Maintenance
+
+This agent handles every step of adding or modifying an anti-pattern detection rule in the impeccable repo. The rule engine is wired into many places and a single source-of-truth design ties them together. Skip any step at your peril — the build's cross-validator will fail loudly if drift slips in.
+
+## The five things that need to stay in sync
+
+When you add a rule, all of these update or get regenerated:
+
+| Where | What | How it stays in sync |
+|---|---|---|
+| `src/detect-antipatterns.mjs` `ANTIPATTERNS` | Rule metadata (id, category, name, description, skillSection, skillGuideline) and the detection logic (`checkXxx`) | **Hand-edited.** Source of truth. |
+| `src/detect-antipatterns-browser.js` | Browser-bundled engine for the public site overlay | Generated by `bun run build:browser` |
+| `extension/detector/detect.js` | Browser-bundled engine for the Chrome extension | Generated by `bun run build:extension` |
+| `extension/detector/antipatterns.json` | Rule list (id, name, category, description) for the extension's devtools panel — drives rule toggles UI | Generated by `bun run build:extension` |
+| `public/js/generated/counts.js` | `DETECTION_COUNT` integer for homepage display | Generated by `bun run build` |
+| `source/skills/impeccable/SKILL.md` | Per-rule **DON'T** line in the right `### Section` — taught to users via the impeccable skill | **Hand-edited.** Validator catches drift. |
+
+The CLI (`bin/cli.js`) imports `ANTIPATTERNS` directly from `src/detect-antipatterns.mjs` — no separate sync needed.
+
+## Rule schema
+
+Each entry in the `ANTIPATTERNS` array (around src/detect-antipatterns.mjs:77) looks like this:
+
+```js
+{
+  id: 'icon-tile-stack',                          // kebab-case, unique, stable
+  category: 'slop',                                // 'slop' or 'quality' (see below)
+  name: 'Icon tile stacked above heading',        // human-readable, used in extension UI
+  description:                                     // 1–2 sentences. Used in CLI output, extension tooltips, web overlay labels
+    'A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.',
+  skillSection: 'Typography',                      // OPTIONAL but strongly recommended. Must be one of the parser's allowed sections (see below)
+  skillGuideline: 'large icons with rounded corners above every heading',  // OPTIONAL but strongly recommended. Substring that must appear in some **DON'T**: line of the named section in source/skills/impeccable/SKILL.md
+}
+```
+
+### Categories
+
+- **`slop`** = "AI tells". Patterns that scream *AI generated this*. Things like purple gradients, gradient text, dark glow accents, thick side borders, icon-tile-stacks. Flagging these is about taste and freshness, not correctness.
+- **`quality`** = real design or accessibility issues regardless of who wrote the code. WCAG contrast, line length, padding, line height, justified text, skipped headings, etc.
+
+If you're not sure, ask: *"would a human designer who's careful and tasteful still ship this?"* If no, it's `quality`. If they would (because it works fine, it just looks templated), it's `slop`.
+
+### `skillSection` allowed values
+
+These are the section names the `readPatterns()` parser at scripts/lib/utils.js:221 understands. Use **exactly** these strings (note the parser normalizes `Color & Theme` → `Color & Contrast`):
+
+```
+Typography
+Color & Contrast
+Layout & Space
+Visual Details
+Motion
+Interaction
+Responsive
+UX Writing
+```
+
+### `skillGuideline` substring
+
+A 3–6 word substring that appears verbatim in some `**DON'T**:` line of the named section in `source/skills/impeccable/SKILL.md`. The build validator checks this with `String.includes()`. Pick a substring that's:
+
+- Short enough that benign rewordings of the DON'T won't break it
+- Specific enough that it can't accidentally collide with an unrelated DON'T
+
+Examples: `'AI color palette'`, `'large icons with rounded corners above every heading'`, `'WCAG AA contrast'`.
+
+If a rule genuinely doesn't deserve a skill DON'T (rare — only the most niche a11y-only rules), omit both `skillSection` and `skillGuideline`. The validator skips rules without `skillGuideline`.
+
+## The TDD recipe (always do it in this order)
+
+This order is non-negotiable. Fixture and failing test before implementation. The full suite must run between the rule going in and you committing.
+
+### 1. Write the fixture (two-column convention)
+
+A single HTML file at `tests/fixtures/antipatterns/{rule-id}.html` with two columns: left = should-flag, right = should-pass. Each test case carries a unique heading text so the test can match snippets back to expectations.
+
+Convention skeleton:
+
+```html
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 960px; margin: 0 auto; padding: 24px; }
+    .col h2 { font-size: 14px; text-transform: uppercase; }
+    /* ... per-case styles with EXPLICIT pixel dimensions (jsdom can't lay out) ... */
+  </style>
+</head>
+<body>
+  <div class="grid">
+    <div class="col" data-col="flag">
+      <h2>Should flag</h2>
+      <!-- 4–6 cases that should be flagged, each with a unique <h3> text -->
+    </div>
+    <div class="col" data-col="pass">
+      <h2>Should pass</h2>
+      <!-- 5–8 cases that should NOT be flagged: cover every false-positive shape you can think of -->
+    </div>
+  </div>
+  <script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>
+```
+
+The script tag at the bottom is critical — it lets you load the fixture in the browser via `http://localhost:3000/fixtures/antipatterns/{rule-id}.html` (served by `server/index.js:62` route for `/fixtures/*`).
+
+**Should-pass cases must cover the false-positive shapes you can think of in advance.** A good fixture has 5+ pass cases. The icon-tile-stack fixture covers: round avatar, wide thumbnail, side-by-side, no-icon, too-tiny, too-huge.
+
+### 2. Write the failing test
+
+Add to `tests/detect-antipatterns-fixtures.test.mjs` in its own `describe` block. Use the snippet-substring matching pattern — the test parses heading text out of each finding's snippet and asserts membership against expected lists:
+
+```js
+describe('detectHtml — {rule-id}', () => {
+  const SHOULD_FLAG = ['Heading One', 'Heading Two', /* ... */];
+  const SHOULD_PASS = ['Pass Heading One', /* ... */];
+
+  it('{rule-id}: flags only the should-flag column', async () => {
+    const f = await detectHtml(path.join(FIXTURES, '{rule-id}.html'));
+    const flagged = new Set();
+    for (const r of f) {
+      if (r.antipattern !== '{rule-id}') continue;
+      const m = (r.snippet || '').match(/"([^"]+)"/);
+      if (m) flagged.add(m[1]);
+    }
+    for (const text of SHOULD_FLAG) {
+      assert.ok(flagged.has(text), `expected "${text}" to be flagged`);
+    }
+    for (const text of SHOULD_PASS) {
+      assert.ok(!flagged.has(text), `"${text}" should NOT be flagged`);
+    }
+  });
+});
+```
+
+For this to work, the rule's snippet **must include the heading text in quotes**. See "Snippet conventions" below.
+
+Run `node --test tests/detect-antipatterns-fixtures.test.mjs` and **watch it fail**. If it doesn't fail, your test is wrong.
+
+### 3. Add the rule definition
+
+Add a new entry to the `ANTIPATTERNS` array in `src/detect-antipatterns.mjs`. Place it in the right category section (slop or quality). Fill in all fields including `skillSection` and `skillGuideline`.
+
+### 4. Implement the pure check function
+
+Add a `checkXxx(opts)` function alongside the others (`checkColors`, `checkBorders`, `checkMotion`, `checkGlow`, `checkIconTile`, etc.). The pure function takes a plain options object — no DOM access — and returns an array of `{ id, snippet }`. This makes it testable and reusable across the browser/Node adapters.
+
+Example shape (see `checkIconTile` in src/detect-antipatterns.mjs for a real one):
+
+```js
+function checkXxx(opts) {
+  const { tag, /* whatever fields the rule needs */ } = opts;
+  if (SAFE_TAGS.has(tag)) return [];
+  // ... your detection logic ...
+  if (matches) {
+    return [{ id: 'rule-id', snippet: `... "${headingText}"` }];
+  }
+  return [];
+}
+```
+
+### 5. Add the two adapters
+
+Two adapters wrap the pure function with environment-specific input gathering:
+
+- **`checkElementXxxDOM(el)`** — for the browser. Uses `getComputedStyle(el)` and `el.getBoundingClientRect()`.
+- **`checkElementXxx(el, tag, window)`** — for jsdom (Node). Uses `window.getComputedStyle(el)` and **must read explicit pixel dimensions from `parseFloat(style.width)`** instead of bounding rects, because **jsdom does not lay out** — `getBoundingClientRect()` returns 0×0 for everything.
+
+If your rule needs vertical positioning info (e.g. "icon must be above heading"), that check is browser-only — gate it behind `if (headingTop && siblingBottom)` so the Node path skips it. The structural checks alone (sizes, sibling identity, classes) are enough for the fixture.
+
+### 6. Wire into both element-iteration loops
+
+Two loops iterate every element on the page. You need to add your DOM-adapter call to **both**:
+
+- **Browser loop** at src/detect-antipatterns.mjs:1837 (`for (const el of document.querySelectorAll('*'))` with the `findings` spread). Add a line like:
+  ```js
+  ...checkElementXxxDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+  ```
+- **Node (jsdom) loop** at src/detect-antipatterns.mjs:2058 (in `detectHtml`). Add a block like:
+  ```js
+  for (const f of checkElementXxx(el, tag, window)) {
+    findings.push(finding(f.id, filePath, f.snippet));
+  }
+  ```
+
+Forgetting one of these is the most common mistake — the test passes but the live page doesn't show anything (or vice versa).
+
+### 7. Add the SKILL.md DON'T line if the rule doesn't reuse an existing one
+
+Open `source/skills/impeccable/SKILL.md`, find the right `### Section`, and add a **DON'T** line that contains your `skillGuideline` substring verbatim. Match the style of existing DON'Ts (terse, prescriptive, 1–2 sentences max).
+
+If your rule reuses an existing DON'T (e.g. multiple engine rules can map to the same skill guidance, like `side-tab` and `border-accent-on-rounded` both pointing to `'thick colored border on one side'`), no skill edit is needed.
+
+### 8. Run the build (this regenerates everything and validates)
+
+```bash
+bun run build && bun run build:browser && bun run build:extension
+```
+
+This regenerates:
+- `src/detect-antipatterns-browser.js` (public-site detector)
+- `extension/detector/detect.js` (extension detector)
+- `extension/detector/antipatterns.json` (extension rule list, includes description)
+- `public/js/generated/counts.js` (DETECTION_COUNT)
+
+And validates:
+- Cross-checks every rule with `skillGuideline` against `source/skills/impeccable/SKILL.md` via `validateAntipatternRules()` in scripts/build.js. **Build fails if drift exists.**
+
+### 9. Run the test suite
+
+```bash
+bun run test
+```
+
+166 unit tests + N fixture tests, including your new one. All should be green.
+
+### 10. Verify on a live page in the browser
+
+Don't skip this. The jsdom path uses `parseFloat(style.width)` and the browser path uses `getBoundingClientRect()` — they can disagree. The fixture test catches one path; manual browser verification catches the other.
+
+```
+http://localhost:3000/fixtures/antipatterns/{rule-id}.html
+http://localhost:3000/antipattern-examples/{your-example}.html  (if relevant)
+http://localhost:3000/                                           (no false positives on real pages)
+```
+
+Use the chrome MCP tools (`mcp__claude-in-chrome__navigate` + `mcp__claude-in-chrome__javascript_tool`) to inject `window.impeccableScan()` and read `.impeccable-overlay` / `.impeccable-label` from the DOM to verify. Don't try to screenshot — the overlays are decorative; read them programmatically.
+
+## Snippet conventions
+
+The fixture-test convention extracts the heading text from a finding's snippet using regex `/"([^"]+)"/` — so **wrap the identifying heading text in straight double quotes** in your snippet. Examples:
+
+- `'80x80px icon tile above h3 "Lightning Fast"'`
+- `'4.5:1 (need 4.5:1) — text #808080 on #3b82f6'`  ← uses element identifiers instead, since this rule isn't anchored to a heading
+
+If your rule isn't naturally anchored to a heading, pick another stable identifier (a class name, the parent element's text, etc.) and document the test pattern in the test itself.
+
+## jsdom constraints (the most common gotcha)
+
+- **No layout.** `getBoundingClientRect()` returns `0×0` always. Read `parseFloat(style.width)` and `parseFloat(style.height)` instead — jsdom does honor explicit pixel widths in `<style>` and inline styles.
+- **`background:` shorthand isn't decomposed.** `style.backgroundColor` and `style.backgroundImage` may be empty even when `style="background: ..."` is set. The existing `resolveBackground()` and `resolveGradientStops()` helpers (src/detect-antipatterns.mjs:631 and src/detect-antipatterns.mjs:670) handle this — use them.
+- **Computed colors are normalized in real browsers, not in jsdom.** A browser returns `rgb(59, 130, 246)`; jsdom may return the original hex. The `parseGradientColors()` helper handles both.
+- **No SAFE_TAGS skipping for parent walks.** When walking ancestors, you don't get the `SAFE_TAGS` filter the main loop applies — be explicit.
+
+## Where to find concrete example rules to learn from
+
+- Simplest border check: **`side-tab`** — `checkBorders()` at src/detect-antipatterns.mjs:312
+- Color/contrast with gradient handling: **`low-contrast`** — `checkColors()` at src/detect-antipatterns.mjs:339
+- Element-relationship check (siblings): **`icon-tile-stack`** — `checkIconTile()` at src/detect-antipatterns.mjs:425
+- Page-level / cross-element: **`flat-type-hierarchy`** — `checkPageTypography()` at src/detect-antipatterns.mjs:1080
+- Motion/animation: **`bounce-easing`** — `checkMotion()` at src/detect-antipatterns.mjs:425
+
+## Pre-commit checklist
+
+Before you commit a new rule, all of these MUST be true. The first three are non-negotiable — if any is missing, the engine, extension, public site, and skill will silently drift apart.
+
+- [ ] Test passes: `bun run test` is green
+- [ ] Build passes: `bun run build && bun run build:browser && bun run build:extension` is green (validator says `✓ Validated N/N anti-pattern rules`)
+- [ ] Live verification: rule fires on a real page and produces zero false positives on the homepage `http://localhost:3000/`
+- [ ] Both element loops were updated (browser DOM at line ~1846 + Node jsdom at line ~2058)
+- [ ] Rule has a corresponding SKILL.md DON'T (or explicitly omitted `skillGuideline`)
+- [ ] Snippet format matches the test's extraction regex
+- [ ] Fixture covers ≥4 should-flag and ≥5 should-pass cases
+- [ ] Commit only the relevant files — `git status` will show many unrelated stale skill builds; do not stage them
+
+## Things that have bitten previous sessions
+
+- **Forgot to run `bun run build:extension`** — extension JSON went stale, missing the new rule. Symptom: extension panel doesn't show toggle for new rule. Fix: always run all three build commands.
+- **Forgot to update both loops** — test passed in jsdom but live browser was silent (or vice versa). Fix: grep for an existing rule's adapter call and copy its placement.
+- **Used a `skillGuideline` substring that doesn't appear in SKILL.md** — validator fails. Fix: the substring must appear verbatim in some `**DON'T**:` line of the named section.
+- **Used the wrong `skillSection` name** — `Color & Theme` vs `Color & Contrast` (parser normalizes the former to the latter, so use `Color & Contrast`).
+- **Wrote the fixture without explicit pixel dimensions** — jsdom returned 0×0 and the rule never matched. Fix: always set `width: Npx; height: Npx` in CSS for fixture elements, or use inline style attributes.

.claude/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,199 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target] [context (mobile, tablet, print...)]"
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.claude/skills/animate/SKILL.md 🔗

@@ -0,0 +1,175 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.claude/skills/audit/SKILL.md 🔗

@@ -0,0 +1,148 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.claude/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,117 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.claude/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,183 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.claude/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,143 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.claude/skills/critique/SKILL.md 🔗

@@ -0,0 +1,227 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+allowed-tools:
+  - Bash(npx impeccable *)
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `CLAUDE.md` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. STOP and call the AskUserQuestion tool to clarify. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.claude/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.claude/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.claude/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `CLAUDE.md` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.claude/skills/delight/SKILL.md 🔗

@@ -0,0 +1,304 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.claude/skills/distill/SKILL.md 🔗

@@ -0,0 +1,122 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.claude/skills/harden/SKILL.md 🔗

@@ -0,0 +1,389 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.claude/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,365 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[craft|teach|extract]"
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .claude/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: Claude is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+STOP and call the AskUserQuestion tool to clarify. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then STOP and call the AskUserQuestion tool to clarify. whether they'd also like the Design Context appended to CLAUDE.md. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.claude/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.claude/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.claude/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, STOP and call the AskUserQuestion tool to clarify. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.claude/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.claude/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.claude/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.claude/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.claude/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.claude/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.claude/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.claude/skills/layout/SKILL.md 🔗

@@ -0,0 +1,125 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.claude/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,266 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.claude/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,142 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **STOP and call the AskUserQuestion tool to clarify.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.claude/skills/polish/SKILL.md 🔗

@@ -0,0 +1,224 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.claude/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,103 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, STOP and call the AskUserQuestion tool to clarify.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.claude/skills/shape/SKILL.md 🔗

@@ -0,0 +1,96 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[feature to shape]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. STOP and call the AskUserQuestion tool to clarify.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+STOP and call the AskUserQuestion tool to clarify. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.claude/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,116 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.codex/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,198 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+argument-hint: "[target] [context (mobile, tablet, print...)]"
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.codex/skills/animate/SKILL.md 🔗

@@ -0,0 +1,174 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.codex/skills/audit/SKILL.md 🔗

@@ -0,0 +1,147 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: $animate, $quieter, $shape, $optimize, $adapt, $clarify, $layout, $distill, $delight, $audit, $harden, $polish, $bolder, $typeset, $critique, $colorize, $overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `$command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `$command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: $animate, $quieter, $shape, $optimize, $adapt, $clarify, $layout, $distill, $delight, $audit, $harden, $polish, $bolder, $typeset, $critique, $colorize, $overdrive. Map findings to the most appropriate command. End with `$polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `$audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.codex/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,116 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.codex/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,182 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.codex/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,142 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.codex/skills/critique/SKILL.md 🔗

@@ -0,0 +1,224 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke $impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run $impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: $animate, $quieter, $shape, $optimize, $adapt, $clarify, $layout, $distill, $delight, $audit, $harden, $polish, $bolder, $typeset, $critique, $colorize, $overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `AGENTS.md` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. ask the user directly to clarify what you cannot infer. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer$bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`$command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`$command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: $animate, $quieter, $shape, $optimize, $adapt, $clarify, $layout, $distill, $delight, $audit, $harden, $polish, $bolder, $typeset, $critique, $colorize, $overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `$polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `$critique` after fixes to see your score improve.

.codex/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.codex/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.codex/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `AGENTS.md` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.codex/skills/delight/SKILL.md 🔗

@@ -0,0 +1,303 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.codex/skills/distill/SKILL.md 🔗

@@ -0,0 +1,121 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.codex/skills/harden/SKILL.md 🔗

@@ -0,0 +1,388 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.codex/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,364 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+argument-hint: "[craft|teach|extract]"
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `$layout`, `/normalize` was folded into `$polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .codex/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run $impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: GPT is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `$impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `$impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+ask the user directly to clarify what you cannot infer. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then ask the user directly to clarify what you cannot infer. whether they'd also like the Design Context appended to AGENTS.md. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `$impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.codex/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.codex/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run $shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run $shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.codex/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, ask the user directly to clarify what you cannot infer. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.codex/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.codex/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.codex/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.codex/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.codex/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.codex/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.codex/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.codex/skills/layout/SKILL.md 🔗

@@ -0,0 +1,124 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.codex/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,265 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.codex/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,141 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **ask the user directly to clarify what you cannot infer.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.codex/skills/polish/SKILL.md 🔗

@@ -0,0 +1,223 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.codex/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,102 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.codex/skills/shape/SKILL.md 🔗

@@ -0,0 +1,95 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+argument-hint: "[feature to shape]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run $impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to $impeccable craft, $impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+ask the user directly to clarify what you cannot infer. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to $impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use $impeccable craft instead, which runs this skill internally.)

.codex/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,115 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+argument-hint: "[target]"
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke $impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run $impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.cursor/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,197 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.cursor/skills/animate/SKILL.md 🔗

@@ -0,0 +1,173 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.cursor/skills/audit/SKILL.md 🔗

@@ -0,0 +1,146 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.cursor/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,115 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.cursor/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,181 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.cursor/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,141 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.cursor/skills/critique/SKILL.md 🔗

@@ -0,0 +1,223 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `.cursorrules` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. ask the user directly to clarify what you cannot infer. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.cursor/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.cursor/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.cursor/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `.cursorrules` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.cursor/skills/delight/SKILL.md 🔗

@@ -0,0 +1,302 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.cursor/skills/distill/SKILL.md 🔗

@@ -0,0 +1,120 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.cursor/skills/harden/SKILL.md 🔗

@@ -0,0 +1,387 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.cursor/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,363 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .cursor/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: the model is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+ask the user directly to clarify what you cannot infer. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then ask the user directly to clarify what you cannot infer. whether they'd also like the Design Context appended to .cursorrules. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.cursor/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.cursor/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.cursor/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, ask the user directly to clarify what you cannot infer. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.cursor/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.cursor/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.cursor/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.cursor/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.cursor/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.cursor/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.cursor/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.cursor/skills/layout/SKILL.md 🔗

@@ -0,0 +1,123 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.cursor/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,264 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.cursor/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,140 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **ask the user directly to clarify what you cannot infer.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.cursor/skills/polish/SKILL.md 🔗

@@ -0,0 +1,222 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.cursor/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,101 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.cursor/skills/shape/SKILL.md 🔗

@@ -0,0 +1,94 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+ask the user directly to clarify what you cannot infer. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.cursor/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,114 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.gemini/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,197 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.gemini/skills/animate/SKILL.md 🔗

@@ -0,0 +1,173 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.gemini/skills/audit/SKILL.md 🔗

@@ -0,0 +1,146 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.gemini/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,115 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.gemini/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,181 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.gemini/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,141 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.gemini/skills/critique/SKILL.md 🔗

@@ -0,0 +1,223 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `GEMINI.md` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. ask the user directly to clarify what you cannot infer. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.gemini/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.gemini/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.gemini/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `GEMINI.md` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.gemini/skills/delight/SKILL.md 🔗

@@ -0,0 +1,302 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.gemini/skills/distill/SKILL.md 🔗

@@ -0,0 +1,120 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.gemini/skills/harden/SKILL.md 🔗

@@ -0,0 +1,387 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.gemini/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,362 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .gemini/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: Gemini is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+ask the user directly to clarify what you cannot infer. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then ask the user directly to clarify what you cannot infer. whether they'd also like the Design Context appended to GEMINI.md. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.gemini/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.gemini/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.gemini/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, ask the user directly to clarify what you cannot infer. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.gemini/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.gemini/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.gemini/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.gemini/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.gemini/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.gemini/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.gemini/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.gemini/skills/layout/SKILL.md 🔗

@@ -0,0 +1,123 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.gemini/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,264 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.gemini/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,140 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **ask the user directly to clarify what you cannot infer.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.gemini/skills/polish/SKILL.md 🔗

@@ -0,0 +1,222 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.gemini/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,101 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.gemini/skills/shape/SKILL.md 🔗

@@ -0,0 +1,94 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+ask the user directly to clarify what you cannot infer. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.gemini/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,114 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.github/CODEOWNERS 🔗

@@ -0,0 +1,8 @@
+# Global ownership — auto-request review from Paul on every PR
+* @pbakaus
+
+# Build system
+scripts/ @pbakaus
+
+# Source content (skills & commands)
+source/ @pbakaus

.github/ISSUE_TEMPLATE/bug_report.md 🔗

@@ -0,0 +1,31 @@
+---
+name: Bug report
+about: Something isn't working as expected
+title: "[Bug] "
+labels: bug
+assignees: ''
+---
+
+## What happened?
+
+<!-- A clear description of the bug. -->
+
+## Steps to reproduce
+
+1. 
+2. 
+3. 
+
+## Expected behavior
+
+<!-- What did you expect to happen? -->
+
+## Provider & environment
+
+- **Provider** (Cursor / Claude Code / Gemini CLI / Codex / Copilot / Kiro / OpenCode):
+- **Provider version**: 
+- **OS**: 
+
+## Additional context
+
+<!-- Paste relevant command output, screenshots, or config snippets here. -->

.github/ISSUE_TEMPLATE/feature_request.md 🔗

@@ -0,0 +1,30 @@
+---
+name: Feature request
+about: Suggest a new command, skill reference, or improvement
+title: "[Feature] "
+labels: enhancement
+assignees: ''
+---
+
+## What problem does this solve?
+
+<!-- Describe the design/UX problem you keep running into. -->
+
+## Proposed solution
+
+<!-- What command, skill update, or anti-pattern guidance would help? -->
+
+## Provider(s) this applies to
+
+- [ ] Cursor
+- [ ] Claude Code
+- [ ] Gemini CLI
+- [ ] Codex CLI
+- [ ] VS Code Copilot
+- [ ] Kiro
+- [ ] OpenCode
+- [ ] All providers
+
+## Alternatives considered
+
+<!-- Any other approaches you thought about? -->

.github/PULL_REQUEST_TEMPLATE.md 🔗

@@ -0,0 +1,21 @@
+## Summary
+
+<!-- What does this PR change and why? -->
+
+## Type of change
+
+- [ ] New command
+- [ ] New / updated skill reference
+- [ ] New anti-pattern guidance
+- [ ] Bug fix
+- [ ] Documentation update
+- [ ] Build system / tooling
+- [ ] Other: 
+
+## Checklist
+
+- [ ] Source files updated in `source/`
+- [ ] `bun run build` ran successfully
+- [ ] `bun test` passes
+- [ ] Tested with at least one provider (Cursor / Claude Code / Gemini CLI / Codex / Copilot / Kiro / OpenCode)
+- [ ] README / DEVELOP.md updated if needed

.github/workflows/ci.yml 🔗

@@ -0,0 +1,41 @@
+name: CI
+
+on:
+  push:
+    branches: [main]
+  pull_request:
+    branches: [main]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        
+      - name: Setup Node
+        uses: actions/setup-node@v4
+        with:
+          node-version: 22
+
+      - name: Setup Bun
+        uses: oven-sh/setup-bun@v2
+        with:
+          bun-version: latest
+
+      - name: Install dependencies
+        run: bun install
+
+      - name: Run tests
+        run: bun run test
+        
+      - name: Build
+        run: bun run build
+        
+      - name: Upload build artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: impeccable-dist
+          path: dist/
+          retention-days: 7

.gitignore 🔗

@@ -0,0 +1,40 @@
+# Dependencies
+node_modules/
+
+# Generated files
+dist/
+build/
+
+# Build artifacts
+*.log
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# IDE
+.vscode/
+.idea/
+.claude/projects/
+
+# Environment
+.env
+.env.local
+
+# Cloudflare
+.wrangler/
+
+# Extension build artifacts
+extension/detector/
+
+# User design context
+.impeccable.md
+
+# Evals (private, commercial)
+evals/
+
+# Generated sub-pages (built from source/skills + content/site at build time)
+public/skills/
+public/anti-patterns/
+public/tutorials/
+public/visual-mode/

.kiro/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,197 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.kiro/skills/animate/SKILL.md 🔗

@@ -0,0 +1,173 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.kiro/skills/audit/SKILL.md 🔗

@@ -0,0 +1,146 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.kiro/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,115 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.kiro/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,181 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.kiro/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,141 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.kiro/skills/critique/SKILL.md 🔗

@@ -0,0 +1,223 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `.kiro/settings.json` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. ask the user directly to clarify what you cannot infer. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.kiro/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.kiro/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.kiro/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `.kiro/settings.json` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.kiro/skills/delight/SKILL.md 🔗

@@ -0,0 +1,302 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.kiro/skills/distill/SKILL.md 🔗

@@ -0,0 +1,120 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.kiro/skills/harden/SKILL.md 🔗

@@ -0,0 +1,387 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.kiro/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,363 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .kiro/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: Claude is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+ask the user directly to clarify what you cannot infer. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then ask the user directly to clarify what you cannot infer. whether they'd also like the Design Context appended to .kiro/settings.json. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.kiro/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.kiro/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.kiro/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, ask the user directly to clarify what you cannot infer. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.kiro/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.kiro/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.kiro/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.kiro/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.kiro/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.kiro/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.kiro/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.kiro/skills/layout/SKILL.md 🔗

@@ -0,0 +1,123 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.kiro/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,264 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.kiro/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,140 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **ask the user directly to clarify what you cannot infer.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.kiro/skills/polish/SKILL.md 🔗

@@ -0,0 +1,222 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.kiro/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,101 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.kiro/skills/shape/SKILL.md 🔗

@@ -0,0 +1,94 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+ask the user directly to clarify what you cannot infer. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.kiro/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,114 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.opencode/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,199 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target] [context (mobile, tablet, print...)]"
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.opencode/skills/animate/SKILL.md 🔗

@@ -0,0 +1,175 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, STOP and call the `question` tool to clarify.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.opencode/skills/audit/SKILL.md 🔗

@@ -0,0 +1,148 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.opencode/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,117 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, STOP and call the `question` tool to clarify.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.opencode/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,183 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.opencode/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,143 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, STOP and call the `question` tool to clarify.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.opencode/skills/critique/SKILL.md 🔗

@@ -0,0 +1,227 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+allowed-tools:
+  - Bash(npx impeccable *)
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `AGENTS.md` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. STOP and call the `question` tool to clarify. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.opencode/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.opencode/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.opencode/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `AGENTS.md` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.opencode/skills/delight/SKILL.md 🔗

@@ -0,0 +1,304 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, STOP and call the `question` tool to clarify.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.opencode/skills/distill/SKILL.md 🔗

@@ -0,0 +1,122 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, STOP and call the `question` tool to clarify.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.opencode/skills/harden/SKILL.md 🔗

@@ -0,0 +1,389 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.opencode/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,365 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[craft|teach|extract]"
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .opencode/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: Claude is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+STOP and call the `question` tool to clarify. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then STOP and call the `question` tool to clarify. whether they'd also like the Design Context appended to AGENTS.md. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.opencode/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.opencode/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.opencode/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, STOP and call the `question` tool to clarify. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.opencode/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.opencode/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.opencode/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.opencode/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.opencode/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.opencode/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.opencode/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.opencode/skills/layout/SKILL.md 🔗

@@ -0,0 +1,125 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.opencode/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,266 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.opencode/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,142 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **STOP and call the `question` tool to clarify.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.opencode/skills/polish/SKILL.md 🔗

@@ -0,0 +1,224 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.opencode/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,103 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, STOP and call the `question` tool to clarify.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.opencode/skills/shape/SKILL.md 🔗

@@ -0,0 +1,96 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[feature to shape]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. STOP and call the `question` tool to clarify.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+STOP and call the `question` tool to clarify. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.opencode/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,116 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.pi/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,197 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.pi/skills/animate/SKILL.md 🔗

@@ -0,0 +1,173 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.pi/skills/audit/SKILL.md 🔗

@@ -0,0 +1,146 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.pi/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,115 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.pi/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,181 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.pi/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,141 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.pi/skills/critique/SKILL.md 🔗

@@ -0,0 +1,225 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+allowed-tools:
+  - Bash(npx impeccable *)
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `AGENTS.md` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. ask the user directly to clarify what you cannot infer. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.pi/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.pi/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.pi/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `AGENTS.md` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.pi/skills/delight/SKILL.md 🔗

@@ -0,0 +1,302 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.pi/skills/distill/SKILL.md 🔗

@@ -0,0 +1,120 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.pi/skills/harden/SKILL.md 🔗

@@ -0,0 +1,387 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.pi/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,363 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .pi/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: the model is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+ask the user directly to clarify what you cannot infer. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then ask the user directly to clarify what you cannot infer. whether they'd also like the Design Context appended to AGENTS.md. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.pi/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.pi/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.pi/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, ask the user directly to clarify what you cannot infer. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.pi/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.pi/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.pi/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.pi/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.pi/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.pi/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.pi/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.pi/skills/layout/SKILL.md 🔗

@@ -0,0 +1,123 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.pi/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,264 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.pi/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,140 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **ask the user directly to clarify what you cannot infer.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.pi/skills/polish/SKILL.md 🔗

@@ -0,0 +1,222 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.pi/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,101 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.pi/skills/shape/SKILL.md 🔗

@@ -0,0 +1,94 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+ask the user directly to clarify what you cannot infer. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.pi/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,114 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.rovodev/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,199 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target] [context (mobile, tablet, print...)]"
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.rovodev/skills/animate/SKILL.md 🔗

@@ -0,0 +1,175 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.rovodev/skills/audit/SKILL.md 🔗

@@ -0,0 +1,148 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.rovodev/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,117 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.rovodev/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,183 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.rovodev/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,143 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.rovodev/skills/critique/SKILL.md 🔗

@@ -0,0 +1,227 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+allowed-tools:
+  - Bash(npx impeccable *)
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `AGENTS.md` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. ask the user directly to clarify what you cannot infer. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.rovodev/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.rovodev/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.rovodev/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `AGENTS.md` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.rovodev/skills/delight/SKILL.md 🔗

@@ -0,0 +1,304 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.rovodev/skills/distill/SKILL.md 🔗

@@ -0,0 +1,122 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.rovodev/skills/harden/SKILL.md 🔗

@@ -0,0 +1,389 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.rovodev/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,365 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[craft|teach|extract]"
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .rovodev/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: Rovo Dev is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+ask the user directly to clarify what you cannot infer. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then ask the user directly to clarify what you cannot infer. whether they'd also like the Design Context appended to AGENTS.md. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.rovodev/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.rovodev/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.rovodev/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, ask the user directly to clarify what you cannot infer. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.rovodev/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.rovodev/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.rovodev/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.rovodev/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.rovodev/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.rovodev/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.rovodev/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.rovodev/skills/layout/SKILL.md 🔗

@@ -0,0 +1,125 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.rovodev/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,266 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.rovodev/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,142 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **ask the user directly to clarify what you cannot infer.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.rovodev/skills/polish/SKILL.md 🔗

@@ -0,0 +1,224 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.rovodev/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,103 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.rovodev/skills/shape/SKILL.md 🔗

@@ -0,0 +1,96 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[feature to shape]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+ask the user directly to clarify what you cannot infer. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.rovodev/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,116 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.trae-cn/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,199 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target] [context (mobile, tablet, print...)]"
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.trae-cn/skills/animate/SKILL.md 🔗

@@ -0,0 +1,175 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.trae-cn/skills/audit/SKILL.md 🔗

@@ -0,0 +1,148 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.trae-cn/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,117 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.trae-cn/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,183 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.trae-cn/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,143 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.trae-cn/skills/critique/SKILL.md 🔗

@@ -0,0 +1,225 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `RULES.md` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. ask the user directly to clarify what you cannot infer. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.trae-cn/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.trae-cn/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.trae-cn/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `RULES.md` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.trae-cn/skills/delight/SKILL.md 🔗

@@ -0,0 +1,304 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.trae-cn/skills/distill/SKILL.md 🔗

@@ -0,0 +1,122 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.trae-cn/skills/harden/SKILL.md 🔗

@@ -0,0 +1,389 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.trae-cn/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,365 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[craft|teach|extract]"
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .trae-cn/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: the model is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+ask the user directly to clarify what you cannot infer. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then ask the user directly to clarify what you cannot infer. whether they'd also like the Design Context appended to RULES.md. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.trae-cn/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.trae-cn/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.trae-cn/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, ask the user directly to clarify what you cannot infer. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.trae-cn/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.trae-cn/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.trae-cn/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.trae-cn/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.trae-cn/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.trae-cn/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.trae-cn/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.trae-cn/skills/layout/SKILL.md 🔗

@@ -0,0 +1,125 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.trae-cn/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,266 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.trae-cn/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,142 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **ask the user directly to clarify what you cannot infer.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.trae-cn/skills/polish/SKILL.md 🔗

@@ -0,0 +1,224 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.trae-cn/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,103 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.trae-cn/skills/shape/SKILL.md 🔗

@@ -0,0 +1,96 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[feature to shape]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+ask the user directly to clarify what you cannot infer. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.trae-cn/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,116 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

.trae/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,199 @@
+---
+name: adapt
+description: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target] [context (mobile, tablet, print...)]"
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.

.trae/skills/animate/SKILL.md 🔗

@@ -0,0 +1,175 @@
+---
+name: animate
+description: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

.trae/skills/audit/SKILL.md 🔗

@@ -0,0 +1,148 @@
+---
+name: audit
+description: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `/command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `/command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive. Map findings to the most appropriate command. End with `/polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

.trae/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,117 @@
+---
+name: bolder
+description: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

.trae/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,183 @@
+---
+name: clarify
+description: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.

.trae/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,143 @@
+---
+name: colorize
+description: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

.trae/skills/critique/SKILL.md 🔗

@@ -0,0 +1,225 @@
+---
+name: critique
+description: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[area (feature, page, component...)]"
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive)
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `RULES.md` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. ask the user directly to clarify what you cannot infer. These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`/command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`/command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: /animate, /quieter, /shape, /optimize, /adapt, /clarify, /layout, /distill, /delight, /audit, /harden, /polish, /bolder, /typeset, /critique, /colorize, /overdrive
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `/polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `/critique` after fixes to see your score improve.

.trae/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

.trae/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

.trae/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `RULES.md` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

.trae/skills/delight/SKILL.md 🔗

@@ -0,0 +1,304 @@
+---
+name: delight
+description: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

.trae/skills/distill/SKILL.md 🔗

@@ -0,0 +1,122 @@
+---
+name: distill
+description: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

.trae/skills/harden/SKILL.md 🔗

@@ -0,0 +1,389 @@
+---
+name: harden
+description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.

.trae/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,365 @@
+---
+name: impeccable
+description: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[craft|teach|extract]"
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node .trae/skills/impeccable/scripts/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run /impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: the model is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `/impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `/impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+ask the user directly to clarify what you cannot infer. Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then ask the user directly to clarify what you cannot infer. whether they'd also like the Design Context appended to RULES.md. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `/impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

.trae/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

.trae/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run /shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run /shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

.trae/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, ask the user directly to clarify what you cannot infer. before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

.trae/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

.trae/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

.trae/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

.trae/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

.trae/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

.trae/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

.trae/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

.trae/skills/layout/SKILL.md 🔗

@@ -0,0 +1,125 @@
+---
+name: layout
+description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

.trae/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,266 @@
+---
+name: optimize
+description: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.

.trae/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,142 @@
+---
+name: overdrive
+description: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **ask the user directly to clarify what you cannot infer.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

.trae/skills/polish/SKILL.md 🔗

@@ -0,0 +1,224 @@
+---
+name: polish
+description: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.

.trae/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,103 @@
+---
+name: quieter
+description: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, ask the user directly to clarify what you cannot infer.
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

.trae/skills/shape/SKILL.md 🔗

@@ -0,0 +1,96 @@
+---
+name: shape
+description: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[feature to shape]"
+---
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to /impeccable craft, /impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. ask the user directly to clarify what you cannot infer.
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+ask the user directly to clarify what you cannot infer. Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to /impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use /impeccable craft instead, which runs this skill internally.)

.trae/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,116 @@
+---
+name: typeset
+description: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.
+version: 2.1.1
+user-invocable: true
+argument-hint: "[target]"
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke /impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run /impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

AGENTS.md 🔗

@@ -0,0 +1,250 @@
+# Impeccable
+
+The vocabulary you didn't know you needed. 1 skill, 18 commands, and curated anti-patterns for impeccable style. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.
+
+## Repository Purpose
+
+Maintain a **single source of truth** for design-focused skills and commands, then automatically transform them into provider-specific formats. Each provider has different capabilities (frontmatter, arguments, modular files), so we use a build system to generate appropriate outputs.
+
+## Architecture: Option A (Feature-Rich Source)
+
+We use a **feature-rich source format** that gets transformed for each provider:
+
+- **Source files** (`source/`): Full metadata with YAML frontmatter, args, descriptions
+- **Build system** (`scripts/`): Transforms source → provider-specific formats
+- **Distribution** (`dist/`): Committed output files for 4 providers
+
+### Why Option A?
+
+Cursor doesn't support frontmatter or arguments (lowest common denominator). Instead of limiting all providers, we:
+1. Author with full metadata in source files
+2. Generate full-featured versions for providers that support it (Claude Code, Gemini, Codex)
+3. Generate downgraded versions for Cursor (strip frontmatter, rely on appending)
+
+## Repository Structure
+
+```
+impeccable/
+├── source/                      # EDIT THESE! Single source of truth
+│   ├── commands/                # Command definitions with frontmatter
+│   │   └── normalize.md
+│   └── skills/                  # Skill definitions with frontmatter
+│       └── impeccable/
+├── dist/                        # Generated outputs (committed for users)
+│   ├── cursor/                  # Commands + Agent Skills
+│   │   └── .cursor/
+│   │       ├── commands/*.md
+│   │       └── skills/*/SKILL.md
+│   ├── claude-code/             # Full featured
+│   │   └── .claude/
+│   │       ├── commands/*.md
+│   │       └── skills/*/SKILL.md
+│   ├── gemini/                  # TOML commands + modular skills
+│   │   ├── .gemini/
+│   │   │   └── commands/*.toml
+│   │   ├── GEMINI.md
+│   │   └── GEMINI.*.md
+│   └── codex/                   # Custom prompts + Agent Skills
+│       └── .codex/
+│           ├── prompts/*.md
+│           └── skills/*/SKILL.md
+├── api/                         # Vercel Functions (production)
+│   ├── skills.js                # GET /api/skills
+│   ├── commands.js              # GET /api/commands
+│   └── download/
+│       ├── [type]/[provider]/[id].js   # Individual downloads
+│       └── bundle/[provider].js        # Bundle downloads
+├── public/                      # Website for impeccable.style
+│   ├── index.html               # Main page
+│   ├── css/                     # Modular CSS (9 files)
+│   │   ├── main.css             # Entry point with imports
+│   │   ├── tokens.css           # Design system
+│   │   └── ...                  # Component styles
+│   └── app.js                   # Vanilla JS
+├── server/                      # Bun server (local dev only)
+│   ├── index.js                 # Serves website + API routes
+│   └── lib/
+│       └── api-handlers.js      # Shared API logic (used by both server & functions)
+├── scripts/                     # Build system (Bun)
+│   ├── build.js                 # Main orchestrator
+│   ├── lib/
+│   │   ├── utils.js             # Shared utilities
+│   │   ├── zip.js               # ZIP generation
+│   │   └── transformers/        # Provider-specific transformers
+│   │       ├── cursor.js
+│   │       ├── claude-code.js
+│   │       ├── gemini.js
+│   │       └── codex.js
+├── README.md                    # End user documentation
+├── DEVELOP.md                   # Contributor documentation
+└── package.json                 # Bun scripts
+```
+
+## Website (impeccable.style)
+
+**Tech Stack:**
+- Vanilla JavaScript (no frameworks)
+- Modern CSS with Bun's bundler (nesting, OKLCH colors, @import)
+- **Local Development**: Bun server with native routes (`server/index.js`)
+- **Production**: Vercel Functions with Bun runtime (`/api` directory)
+- Deployed on Vercel with Bun runtime
+
+**Dual Setup:**
+- `/api` directory contains individual Vercel Functions for production
+- `/server` directory contains monolithic Bun server for local development
+- `/server/lib/api-handlers.js` contains shared logic used by both
+- Zero duplication: API functions and dev server import the same handlers
+
+**Design:**
+- Editorial precision aesthetic
+- Cormorant Garamond (display) + Instrument Sans (body)
+- OKLCH color space for vibrant, perceptually uniform colors
+- Editorial sidebar layout (title left, content right)
+- Modular CSS architecture (9 files)
+
+**API Endpoints** (Vercel Functions):
+- `/` - Homepage (static HTML)
+- `/api/skills` - JSON list of all skills
+- `/api/commands` - JSON list of all commands
+- `/api/download/[type]/[provider]/[id]` - Individual file download
+- `/api/download/bundle/[provider]` - ZIP bundle download
+
+## Source File Format
+
+### Commands (`source/commands/*.md`)
+
+```yaml
+---
+name: command-name
+description: Clear description of what this command does
+args:
+  - name: argname
+    description: Argument description
+    required: false
+---
+
+Command prompt here. Use {{argname}} placeholders for arguments.
+```
+
+### Skills (`source/skills/*.md`)
+
+```yaml
+---
+name: skill-name
+description: Clear description of what this skill provides
+license: License info (optional)
+---
+
+Skill instructions for the LLM here.
+```
+
+## Build System
+
+Uses **Bun** for fast builds. Modular architecture:
+
+- **`utils.js`**: Shared functions (parseFrontmatter, readSourceFiles, writeFile, etc.)
+- **Transformer pattern**: Each provider has one focused file
+- **Registry**: `transformers/index.js` exports all transformers
+- **Main script**: `build.js` orchestrates everything (~50 lines)
+
+Run: `bun run build`
+
+## Provider Transformations
+
+### 1. Cursor (Agent Skills Standard)
+- **Commands**: Body only → `dist/cursor/.cursor/commands/*.md` (no frontmatter support)
+- **Skills**: Agent Skills standard → `dist/cursor/.cursor/skills/{name}/SKILL.md`
+  - Full YAML frontmatter with name/description
+  - Reference files in skill subdirectories
+- **Installation**: Extract ZIP into your project root, creates `.cursor/` folder
+- **Note**: Agent Skills require Cursor nightly channel
+
+### 2. Claude Code (Full Featured)
+- **Commands**: Full YAML frontmatter → `dist/claude-code/.claude/commands/*.md`
+- **Skills**: Full YAML frontmatter → `dist/claude-code/.claude/skills/{name}/SKILL.md`
+- **Preserves**: All metadata, all args
+- **Format**: Matches [Anthropic Skills spec](https://github.com/anthropics/skills)
+- **Installation**: Extract ZIP into your project root, creates `.claude/` folder
+
+### 3. Gemini CLI (Full Featured)
+- **Commands**: TOML format → `dist/gemini/.gemini/commands/*.toml`
+  - Uses `description` and `prompt` keys
+  - Transforms `{{argname}}` → `{{args}}` (Gemini uses single args string)
+- **Skills**: Modular with imports → `dist/gemini/GEMINI.{name}.md` (root level)
+  - Main `GEMINI.md` uses `@./GEMINI.{name}.md` import syntax
+  - Gemini automatically loads imported files
+- **Installation**: Extract ZIP into your project root, creates `.gemini/` folder + skill files
+
+### 4. Codex CLI (Full Featured)
+- **Commands**: Custom prompt format → `dist/codex/.codex/prompts/*.md`
+  - Uses `description` and `argument-hint` in frontmatter
+  - Transforms `{{argname}}` → `$ARGNAME` (uppercase variables)
+  - Invoked as `/prompts:<name>`
+- **Skills**: Agent Skills standard → `dist/codex/.codex/skills/{name}/SKILL.md`
+  - Same SKILL.md format as Claude Code with YAML frontmatter
+  - Reference files in skill subdirectories
+- **Installation**: Extract ZIP into your project root, creates `.codex/` folder
+
+## Key Design Decisions
+
+### Why commit dist/?
+End users can copy files directly without needing build tools.
+
+### Why separate transformers?
+- Each provider ~30-85 lines, easy to understand
+- Can modify one without affecting others
+- Easy to add new providers
+
+### Why Bun?
+- Much faster than Node.js (2-4x)
+- All-in-one toolkit (runtime + package manager)
+- Zero config, TypeScript native
+- Node.js compatible (works with existing code)
+
+### Why modular skills for Gemini/Codex?
+- Better context management (load only what's needed)
+- Cleaner file organization
+- Gemini: Uses native `@file.md` import feature
+- Codex: Uses routing pattern with AGENTS.md guide
+
+### Why vanilla JS for website?
+- No build complexity
+- Bun handles everything natively
+- Modern features (ES6+, CSS nesting, OKLCH colors)
+- Fast, lean, maintainable
+
+## Adding New Content
+
+1. **Create source file** in `source/commands/` or `source/skills/`
+2. **Add frontmatter** with name, description, args (for commands) or license (for skills)
+3. **Write body** with instructions/prompt
+4. **Build**: `bun run build`
+5. **Test** with your provider
+6. **Commit** both source and dist files
+
+## Important Notes
+
+- **Source is truth**: Always edit `source/`, never edit `dist/` directly
+- **Test across providers**: Changes affect 4 different outputs
+- **Argument handling**: Write prompts that work with both placeholders and appending
+- **Cursor limitations**: No frontmatter/args, so design for graceful degradation
+
+## Documentation
+
+- **README.md**: End user guide (installation, usage, quick dev setup)
+- **DEVELOP.md**: Contributor guide (architecture, build system, adding content)
+- **This file (AGENTS.md)**: Context for AI assistants and new developers
+
+## Provider Documentation Links
+
+- [Agent Skills Specification](https://agentskills.io/specification) - Open standard
+- [Cursor Commands](https://cursor.com/docs/agent/chat/commands)
+- [Cursor Rules](https://cursor.com/docs/context/rules)
+- [Cursor Skills](https://cursor.com/docs/context/skills)
+- [Claude Code Slash Commands](https://code.claude.com/docs/en/slash-commands)
+- [Anthropic Skills](https://github.com/anthropics/skills)
+- [Gemini CLI Custom Commands](https://cloud.google.com/blog/topics/developers-practitioners/gemini-cli-custom-slash-commands)
+- [Gemini CLI GEMINI.md](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/gemini-md.md)
+- [Codex CLI Slash Commands](https://developers.openai.com/codex/guides/slash-commands)
+- [Codex CLI Skills](https://developers.openai.com/codex/skills/)
+

CLAUDE.md 🔗

@@ -0,0 +1,151 @@
+# Project Instructions for Claude
+
+## CSS
+
+Plain hand-written CSS, no Tailwind, no build step. Bun's HTML loader resolves
+`<link rel="stylesheet">` and inlines `@import` chains automatically for both
+`bun run dev` and `bun run build`.
+
+The CSS architecture:
+- `public/css/main.css` - Main entry point, imports the partials and defines tokens/reset
+- `public/css/workflow.css` - Commands section, glass terminal, case studies styles
+- `public/css/gallery.css`, `skill-demos.css`, `problem-section.css` - section partials
+
+Edit any of these directly and reload — no rebuild needed.
+
+## Development Server
+
+```bash
+bun run dev        # Bun dev server at http://localhost:3000
+bun run preview    # Build + Cloudflare Pages local preview
+```
+
+## Deployment
+
+Hosted on Cloudflare Pages. Static assets served from `build/`, API routes handled via `_redirects` rewrites (JSON) and Pages Functions (downloads).
+
+```bash
+bun run deploy     # Build + deploy to Cloudflare Pages
+```
+
+## Build System
+
+The build system compiles skills and commands from `source/` to provider-specific formats in `dist/`:
+
+```bash
+bun run build      # Build all providers
+bun run rebuild    # Clean and rebuild
+```
+
+Source files use placeholders that get replaced per-provider:
+- `{{model}}` - Model name (Claude, Gemini, GPT, etc.)
+- `{{config_file}}` - Config file name (CLAUDE.md, .cursorrules, etc.)
+- `{{ask_instruction}}` - How to ask user questions
+
+## Testing
+
+```bash
+bun run test       # Run all tests
+```
+
+Unit tests (build, detector logic) run via `bun test`. Fixture tests (jsdom-based HTML detection) run via `node --test` because bun is too slow with jsdom. The `test` script handles this split automatically.
+
+## CLI
+
+The CLI lives in this repo under `bin/` and `src/`. Published to npm as `impeccable`.
+
+```bash
+npx impeccable detect [file-or-dir-or-url...]   # detect anti-patterns
+npx impeccable detect --fast --json src/         # regex-only, JSON output
+npx impeccable live                              # start browser overlay server
+npx impeccable skills install                    # install skills
+npx impeccable --help                            # show help
+```
+
+The browser detector (`src/detect-antipatterns-browser.js`) is generated from the main engine. After changing `src/detect-antipatterns.mjs`, rebuild it:
+
+```bash
+bun run build:browser
+```
+
+**IMPORTANT**: Always use `node` (not `bun`) to run the detect CLI. Bun's jsdom implementation is extremely slow and will cause scans with HTML files to hang for minutes.
+
+## Versioning
+
+There are three independently versioned components. Only bump the one(s) that actually changed:
+
+**CLI** (npm package):
+- `package.json` → `version`
+- Bump when: CLI code changes (`bin/`, `src/detect-antipatterns.mjs`, etc.)
+
+**Skills** (Claude Code plugin / skill definitions):
+- `.claude-plugin/plugin.json` → `version`
+- `.claude-plugin/marketplace.json` → `plugins[0].version`
+- Bump when: skill content changes (`source/skills/`, skill count changes, etc.)
+
+**Chrome extension**:
+- `extension/manifest.json` → `version`
+- Bump when: extension code changes (`extension/`)
+
+**Website changelog** (`public/index.html`):
+- Hero version link text + new changelog entry
+- Update for user-facing changes only, not internal build/tooling details
+- Use the most prominent version that changed (e.g. skills version for skill consolidation)
+
+## Adding New Skills
+
+When adding a new user-invocable skill, update the command count in **all** of these locations:
+
+- `public/index.html` → meta descriptions, hero box, section lead
+- `public/cheatsheet.html` → meta description, subtitle, `commandCategories`, `commandRelationships`
+- `public/js/data.js` → `commandProcessSteps`, `commandCategories`, `commandRelationships`
+- `public/js/components/framework-viz.js` → `commandSymbols`, `commandNumbers`
+- `public/js/demos/commands/` → new demo file + import in `index.js`
+- `README.md` → intro, command count, commands table
+- `NOTICE.md` → steering commands count
+- `AGENTS.md` → intro command count
+- `.claude-plugin/plugin.json` → description
+- `.claude-plugin/marketplace.json` → metadata description + plugin description
+
+## Evals Framework (private, gitignored)
+
+There is a controlled eval framework at `evals/` that measures whether the `/impeccable` skill improves or harms AI-generated frontend design. It runs the same brief through a model with and without the skill loaded, fingerprints every generation, and aggregates the results into a bias report. The whole `evals/` directory is gitignored — it's intended to stay private (commercial).
+
+**If you're picking up eval work in a new session, read `evals/AGENT.md` first.** It captures everything we've learned: model choices, sample size policy, lessons learned, common workflows, and gotchas. Don't try to reinvent the workflow from scratch — there's significant prior context.
+
+### Quick orientation
+
+- **Primary baseline model**: `gpt-5.4` with `--reasoning-effort medium`. Frontier intelligence at ~5-10× lower cost than high reasoning. **Do NOT use `--reasoning-effort high`** unless you specifically need it — reasoning tokens count against `max_completion_tokens` and burn ~$1-2/file with no quality benefit for our use case.
+- **Secondary validation model**: `qwen/qwen3.6-plus` via OpenRouter. Cheap-ish, decent design quality, no reasoning controls.
+- **Do NOT use Haiku as a primary eval target.** It ignores most negative rules in the skill. We learned this the hard way — it sent us down many wrong paths early on.
+- **Sample size policy**: n=10 per niche for scratch iteration, **n=20 for sweep validation (the standard)**, n=50 reserved for the final published baseline. n=20 is the smallest sample where rare detector findings stabilize and A/B comparisons are statistically meaningful.
+
+### Quick commands
+
+```bash
+# Always start the local server first — the gallery/viewer can't load via file:// (CORS)
+bun run evals/runner/serve.ts
+
+# Standard workflow: generate → detect → aggregate → snapshot
+bun run evals/runner/run.ts --with-refs --model gpt-5.4 --reasoning-effort medium
+bun run evals/runner/detect.ts
+bun run evals/runner/aggregate.ts
+bun run evals/runner/snapshot.ts <slug> --title "..." --note "..."
+
+# Cheap targeted iteration (does not pollute current/)
+bun run evals/runner/run.ts --with-refs --scratch my-test \
+  --niches 06 --n 10 --condition skill-on --model qwen/qwen3.6-plus
+
+# View results in browser
+open http://localhost:8723/viewer.html
+```
+
+### Critical rules
+
+- **Always run a small smoke test (n=2-5 on one niche) before any sweep.** Rate degrades over long runs and time estimates can be off by 10-20×. We once burned 11+ hours on a sweep estimated to take 40 minutes.
+- **Background long runs.** Use `run_in_background: true` for any sweep over ~50 generations. The runner is resumable so killing and restarting is safe.
+- **Don't mix prompt versions in the same dataset.** The variant.json safety check enforces this for `current/` (must pass `--rebuild-skill-on` after a prompt edit). Scratch dirs auto-wipe on prompt change.
+- **Snapshot first, change second.** Always have a known reference point in `evals/output/snapshots/` before editing the skill, so you can compare before/after.
+- **The user is the source of truth on aesthetic quality.** The fingerprinter and detector are useful signals but do not measure "is this design good?" Have the user spot-check the gallery for any meaningful change.
+
+See `evals/AGENT.md` for the full reference: detailed model comparison table, complete lessons learned, all common workflows, and the list of gotchas.

DEVELOP.md 🔗

@@ -0,0 +1,206 @@
+# Developer Guide
+
+Documentation for contributors to Impeccable.
+
+## Architecture
+
+Source skills in `source/skills/` are transformed into provider-specific formats by a config-driven factory. Each provider is defined as a config object in `scripts/lib/transformers/providers.js` -- adding a new provider requires only a new config entry.
+
+For detailed harness capabilities (which frontmatter fields each supports, placeholder systems, directory structures), see [HARNESSES.md](HARNESSES.md).
+
+## Source Format
+
+### Skills (`source/skills/{name}/SKILL.md`)
+
+```yaml
+---
+name: skill-name
+description: What this skill provides
+argument-hint: "[target]"
+user-invocable: true
+license: License info (optional)
+compatibility: Environment requirements (optional)
+---
+
+Your skill instructions here...
+```
+
+**Frontmatter fields** (based on [Agent Skills spec](https://agentskills.io/specification)):
+- `name` (required): Skill identifier (1-64 chars, lowercase/numbers/hyphens)
+- `description` (required): What the skill provides (1-1024 chars)
+- `user-invocable` (optional): Boolean -- if `true`, the skill can be invoked as a slash command
+- `argument-hint` (optional): Hint shown during autocomplete (e.g., `[target]`, `[area (feature, page...)]`)
+- `license` (optional): License/attribution info
+- `compatibility` (optional): Environment requirements (1-500 chars)
+- `metadata` (optional): Arbitrary key-value pairs
+- `allowed-tools` (optional, experimental): Pre-approved tools list
+
+**Body placeholders** (replaced per-provider during build):
+- `{{model}}` -- Provider-specific model name (e.g., "Claude", "Gemini", "GPT")
+- `{{config_file}}` -- Provider-specific config file (e.g., "CLAUDE.md", ".cursorrules")
+- `{{ask_instruction}}` -- How to ask the user for clarification
+- `{{command_prefix}}` -- Slash command prefix (`/` for most, `$` for Codex)
+- `{{available_commands}}` -- Comma-separated list of user-invocable commands
+
+## Building
+
+### Prerequisites
+- Bun (fast JavaScript runtime and package manager)
+- No external dependencies required
+
+### Commands
+
+```bash
+# Build all provider formats
+bun run build
+
+# Clean dist folder
+bun run clean
+
+# Rebuild from scratch
+bun run rebuild
+```
+
+### What Gets Generated
+
+```
+source/                          -> dist/
+  skills/{name}/SKILL.md           {provider}/{configDir}/skills/{name}/SKILL.md
+```
+
+Each provider gets its own output directory. Two variants are generated per provider: unprefixed and prefixed (with `i-` prefix for skill names).
+
+## Build System Details
+
+The build system uses a factory pattern under `scripts/`:
+
+```
+scripts/
+  build.js                        # Main orchestrator
+  lib/
+    utils.js                      # Frontmatter parsing, placeholder replacement, YAML generation
+    zip.js                        # ZIP bundle generation
+    transformers/
+      factory.js                  # createTransformer() -- generates transformer functions from config
+      providers.js                # PROVIDERS config map -- one entry per provider
+      index.js                    # Re-exports factory-generated transformer functions
+```
+
+### Adding a New Provider
+
+1. Add a placeholder config to `PROVIDER_PLACEHOLDERS` in `scripts/lib/utils.js`:
+   ```javascript
+   'my-provider': {
+     model: 'MyModel',
+     config_file: 'CONFIG.md',
+     ask_instruction: 'ask the user directly to clarify.',
+     command_prefix: '/'
+   }
+   ```
+
+2. Add a provider config to `PROVIDERS` in `scripts/lib/transformers/providers.js`:
+   ```javascript
+   'my-provider': {
+     provider: 'my-provider',
+     configDir: '.my-provider',
+     displayName: 'My Provider',
+     frontmatterFields: ['user-invocable', 'argument-hint', 'license'],
+   }
+   ```
+
+3. Run `bun run build` -- the provider is automatically picked up by the build loop.
+
+4. Update `HARNESSES.md` with the provider's capabilities.
+
+### Provider Config Options
+
+| Field | Description |
+|-------|-------------|
+| `provider` | Key for output directory and placeholder lookup |
+| `configDir` | Dot-directory name (e.g., `.claude`) |
+| `displayName` | Human-readable name for build logs |
+| `frontmatterFields` | Which optional fields to emit (see `factory.js` FIELD_SPECS) |
+| `bodyTransform` | Optional `(body, skill) => body` function for post-processing |
+| `placeholderProvider` | Override which PROVIDER_PLACEHOLDERS key to use (for variants sharing config) |
+
+### Key Functions
+
+- `createTransformer(config)`: Factory that returns a transformer function from a provider config
+- `parseFrontmatter()`: Extracts YAML frontmatter and body from SKILL.md files
+- `readSourceFiles()`: Reads all skill directories from `source/skills/`
+- `replacePlaceholders()`: Substitutes `{{model}}`, `{{config_file}}`, etc. per provider
+- `generateYamlFrontmatter()`: Serializes objects to YAML frontmatter (auto-quotes values starting with `[` or `{`)
+- `prefixSkillReferences()`: Replaces `/skillname` with `/i-skillname` for prefixed variants
+
+## Best Practices
+
+### Skill Writing
+
+1. **Focused scope**: One clear domain per skill
+2. **Clear descriptions**: Make purpose obvious
+3. **Clear instructions**: LLM should understand exactly what to do
+4. **Include examples**: Where they clarify intent
+5. **State constraints**: What NOT to do as clearly as what to do
+6. **Test across providers**: Verify it works in multiple contexts
+
+## Reference Documentation
+
+- [Agent Skills Specification](https://agentskills.io/specification) - Open standard
+- [HARNESSES.md](HARNESSES.md) - Provider capabilities matrix
+- [Cursor Skills](https://cursor.com/docs/context/skills)
+- [Claude Code Skills](https://code.claude.com/docs/en/skills)
+- [Gemini CLI Skills](https://geminicli.com/docs/cli/skills/)
+- [Codex CLI Skills](https://developers.openai.com/codex/skills/)
+- [VS Code Copilot Skills](https://code.visualstudio.com/docs/copilot/customization/agent-skills)
+- [Kiro Skills](https://kiro.dev/docs/skills/)
+- [OpenCode Skills](https://opencode.ai/docs/skills/)
+- [Pi Skills](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/skills.md)
+
+## Repository Structure
+
+```
+impeccable/
+  source/                          # Edit these! Source of truth
+    skills/                        # Skill definitions
+      frontend-design/
+        SKILL.md
+        reference/*.md             # Domain-specific references
+      audit/SKILL.md
+      polish/SKILL.md
+      ...
+  dist/                            # Generated output (gitignored)
+  scripts/
+    build.js                       # Main orchestrator
+    lib/
+      utils.js                     # Shared utilities
+      zip.js                       # ZIP generation
+      transformers/
+        factory.js                 # Config-driven transformer factory
+        providers.js               # Provider config map
+        index.js                   # Re-exports
+  tests/                           # Bun test suite
+  HARNESSES.md                     # Provider capabilities reference
+  DEVELOP.md                       # This file
+  README.md                        # User documentation
+```
+
+## Troubleshooting
+
+### Build fails with YAML parsing errors
+- Check frontmatter indentation (YAML is indent-sensitive)
+- Ensure `---` delimiters are on their own lines
+- Values starting with `[` or `{` are auto-quoted; other special YAML chars may need manual quoting
+
+### Output doesn't match expectations
+- Check the provider config in `scripts/lib/transformers/providers.js`
+- Verify source file has correct frontmatter structure
+- Run `bun run rebuild` to ensure clean build
+
+### Provider doesn't recognize the files
+- Check installation path for your provider
+- Verify file naming matches provider requirements
+- Consult [HARNESSES.md](HARNESSES.md) for provider-specific details
+
+## Questions?
+
+Open an issue or submit a PR!

HARNESSES.md 🔗

@@ -0,0 +1,86 @@
+# Harness Skills Capabilities Reference
+
+Source of truth for what each AI coding harness supports in terms of agent skills.
+Used to inform provider configs in `scripts/lib/transformers/providers.js`.
+
+Last verified: 2026-03-24
+
+## Official Documentation
+
+| Harness | Docs URL |
+|---------|----------|
+| Claude Code | https://code.claude.com/docs/en/skills |
+| Cursor | https://cursor.com/docs/context/skills |
+| Gemini CLI | https://geminicli.com/docs/cli/skills/ |
+| Codex CLI | https://developers.openai.com/codex/skills |
+| GitHub Copilot (Agents) | https://code.visualstudio.com/docs/copilot/customization/agent-skills |
+| Kiro | https://kiro.dev/docs/skills/ |
+| OpenCode | https://opencode.ai/docs/skills/ |
+| Pi | https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/skills.md |
+| Trae | TBD (no official skills docs found yet) |
+| Rovo Dev | https://support.atlassian.com/rovo/docs/extend-rovo-dev-cli-with-agent-skills |
+
+## Spec Compliance
+
+All harnesses follow the [Agent Skills specification](https://agentskills.io/specification) to varying degrees. The spec defines these frontmatter fields: `name`, `description`, `license`, `compatibility`, `metadata`, `allowed-tools`.
+
+Provider-specific extensions beyond the spec: `user-invocable`, `argument-hint`, `disable-model-invocation`, `allowed-tools` (extended syntax), `model`, `effort`, `context`, `agent`, `hooks`, `subtask`, `mcp`.
+
+## Frontmatter Support
+
+Fields marked with * are spec-standard. Others are provider extensions.
+
+| Field | Claude Code | Cursor | Gemini | Codex | Copilot | Kiro | OpenCode | Pi | Rovo Dev |
+|-------|:-----------:|:------:|:------:|:-----:|:-------:|:----:|:--------:|:--:|:--------:|
+| `name`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
+| `description`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
+| `license`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes |
+| `compatibility`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes |
+| `metadata`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes |
+| `allowed-tools`* | Yes | No | Ignored | No | No | No | Yes | Yes | Yes |
+| `user-invocable` | Yes | No | No | No | Yes | No | Yes | No | Yes |
+| `argument-hint` | Yes | No | No | No | Yes | No | Yes | No | Yes |
+| `disable-model-invocation` | Yes | Yes | No | No | Yes | No | Yes | Yes | TBD |
+| `model` | Yes | No | No | No | No | No | Yes | No | No |
+| `effort` | Yes | No | No | No | No | No | No | No | No |
+| `context` | Yes | No | No | No | No | No | No | No | No |
+| `agent` | Yes | No | No | No | No | No | Yes | No | No |
+| `hooks` | Yes | No | No | No | No | No | No | No | No |
+
+Notes:
+- Gemini CLI validates only `name` and `description`; other spec fields are parsed but ignored.
+- Codex CLI uses a separate `agents/openai.yaml` sidecar for extended metadata (icons, branding, MCP tools, invocation control).
+- Kiro recognizes `user-invocable` and `disable-model-invocation` per community reports but does not formally document them.
+- Unknown fields are silently ignored by all harnesses.
+
+## Skill Directory Structure
+
+| Harness | Native directory | Also reads |
+|---------|-----------------|------------|
+| Claude Code | `.claude/skills/` | - |
+| Cursor | `.cursor/skills/` | `.agents/skills/`, `.claude/skills/`, `.codex/skills/` |
+| Gemini CLI | `.gemini/skills/` | `.agents/skills/` |
+| Codex CLI | `.agents/skills/` (primary) | - |
+| GitHub Copilot | `.github/skills/` | `.agents/skills/`, `.claude/skills/` |
+| Kiro | `.kiro/skills/` | - |
+| OpenCode | `.opencode/skills/` | `.agents/skills/`, `.claude/skills/` |
+| Pi | `.pi/skills/` | `.agents/skills/` |
+| Trae China | `.trae-cn/skills/` | TBD |
+| Trae International | `.trae/skills/` | TBD |
+| Rovo Dev | `.rovodev/skills/` | `~/.rovodev/skills/` (user-level) |
+
+All harnesses support the `{skill-name}/SKILL.md` directory structure with optional `reference/`, `scripts/`, and `assets/` subdirectories.
+
+## Placeholder / Variable Substitution
+
+Claude Code supports runtime variable substitution directly in SKILL.md bodies: `$ARGUMENTS`, `$0`-`$N`, `${CLAUDE_SKILL_DIR}`, `${CLAUDE_SESSION_ID}`. No other harness supports substitution in skills.
+
+Some harnesses have separate "custom commands" systems (distinct from skills) with their own substitution:
+
+| Harness | Command system | Substitution syntax |
+|---------|---------------|-------------------|
+| Gemini CLI | `.gemini/commands/` (TOML) | `{{args}}`, `!{shell}`, `@{file}` |
+| Codex CLI | `.codex/prompts/` | `$ARGNAME` |
+| OpenCode | `.opencode/commands/` | `$ARGUMENTS`, `$1`-`$N`, `` !`shell` `` |
+
+Our build system handles cross-provider placeholders at compile time via `replacePlaceholders()` for `{{model}}`, `{{config_file}}`, `{{ask_instruction}}`, and `{{available_commands}}`.

LICENSE 🔗

@@ -0,0 +1,191 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to the Licensor for inclusion in the Work by the copyright
+      owner or by an individual or Legal Entity authorized to submit on
+      behalf of the copyright owner. For the purposes of this definition,
+      "submitted" means any form of electronic, verbal, or written
+      communication sent to the Licensor or its representatives, including
+      but not limited to communication on electronic mailing lists, source
+      code control systems, and issue tracking systems that are managed by,
+      or on behalf of, the Licensor for the purpose of discussing and
+      improving the Work, but excluding communication that is conspicuously
+      marked or otherwise designated in writing by the copyright owner as
+      "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   Copyright 2025 Paul Bakaus
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

NOTICE.md 🔗

@@ -0,0 +1,17 @@
+# Notice
+
+Impeccable
+Copyright 2025-2026 Paul Bakaus
+
+## Anthropic frontend-design Skill
+
+The `impeccable` skill in this project builds on Anthropic's original frontend-design skill.
+
+**Original work:** https://github.com/anthropics/skills/tree/main/skills/frontend-design
+**Original license:** Apache License 2.0
+**Copyright:** 2025 Anthropic, PBC
+
+This project extends the original with:
+- 7 domain-specific reference files (typography, color-and-contrast, spatial-design, motion-design, interaction-design, responsive-design, ux-writing)
+- 18 steering commands
+- Expanded patterns and anti-patterns

README.md 🔗

@@ -0,0 +1,246 @@
+# Impeccable
+
+The vocabulary you didn't know you needed. 1 skill, 18 commands, and curated anti-patterns for impeccable frontend design.
+
+> **Quick start:** Visit [impeccable.style](https://impeccable.style) to download ready-to-use bundles.
+
+## Why Impeccable?
+
+Anthropic created [frontend-design](https://github.com/anthropics/skills/tree/main/skills/frontend-design), a skill that guides Claude toward better UI design. Impeccable builds on that foundation with deeper expertise and more control.
+
+Every LLM learned from the same generic templates. Without guidance, you get the same predictable mistakes: Inter font, purple gradients, cards nested in cards, gray text on colored backgrounds.
+
+Impeccable fights that bias with:
+- **An expanded skill** with 7 domain-specific reference files ([view source](source/skills/impeccable/))
+- **18 steering commands** to audit, review, polish, distill, animate, and more
+- **Curated anti-patterns** that explicitly tell the AI what NOT to do
+
+## What's Included
+
+### The Skill: impeccable
+
+A comprehensive design skill with 7 domain-specific references ([view skill](source/skills/impeccable/SKILL.md)):
+
+| Reference | Covers |
+|-----------|--------|
+| [typography](source/skills/impeccable/reference/typography.md) | Type systems, font pairing, modular scales, OpenType |
+| [color-and-contrast](source/skills/impeccable/reference/color-and-contrast.md) | OKLCH, tinted neutrals, dark mode, accessibility |
+| [spatial-design](source/skills/impeccable/reference/spatial-design.md) | Spacing systems, grids, visual hierarchy |
+| [motion-design](source/skills/impeccable/reference/motion-design.md) | Easing curves, staggering, reduced motion |
+| [interaction-design](source/skills/impeccable/reference/interaction-design.md) | Forms, focus states, loading patterns |
+| [responsive-design](source/skills/impeccable/reference/responsive-design.md) | Mobile-first, fluid design, container queries |
+| [ux-writing](source/skills/impeccable/reference/ux-writing.md) | Button labels, error messages, empty states |
+
+### 18 Commands
+
+| Command | What it does |
+|---------|--------------|
+| `/impeccable teach` | One-time setup: gather design context, save to config |
+| `/impeccable craft` | Full shape-then-build flow with visual iteration |
+| `/impeccable extract` | Pull reusable components and tokens into the design system |
+| `/audit` | Run technical quality checks (a11y, performance, responsive) |
+| `/critique` | UX design review: hierarchy, clarity, emotional resonance |
+| `/polish` | Final pass, design system alignment, and shipping readiness |
+| `/distill` | Strip to essence |
+| `/clarify` | Improve unclear UX copy |
+| `/optimize` | Performance improvements |
+| `/harden` | Error handling, onboarding, i18n, edge cases |
+| `/animate` | Add purposeful motion |
+| `/colorize` | Introduce strategic color |
+| `/bolder` | Amplify boring designs |
+| `/quieter` | Tone down overly bold designs |
+| `/delight` | Add moments of joy |
+| `/adapt` | Adapt for different devices |
+| `/typeset` | Fix font choices, hierarchy, sizing |
+| `/layout` | Fix layout, spacing, visual rhythm |
+| `/overdrive` | Add technically extraordinary effects |
+
+#### Usage Examples
+
+**`/audit`** - Run quality checks, get a report (no edits)
+```
+/audit blog              # Audit blog hub + post pages
+/audit dashboard         # Check dashboard components
+/audit checkout flow     # Focus on checkout UX
+```
+*When to use:* Before making changes, to understand what needs fixing.
+
+**`/normalize`** - Align with design system
+```
+/normalize blog          # Apply design tokens, fix spacing
+/normalize buttons       # Standardize button styles
+```
+*When to use:* After audit, to fix inconsistencies.
+
+**`/critique`** - UX design review
+```
+/critique landing page   # Review landing page UX
+/critique onboarding     # Check onboarding flow
+```
+*When to use:* When you want design feedback, not technical fixes.
+
+**`/polish`** - Final pass before shipping
+```
+/polish feature modal    # Clean up modal before release
+/polish settings page    # Final review of settings UI
+```
+*When to use:* Last step before deploying to production.
+
+**Combining commands:**
+```
+/audit /normalize /polish blog    # Full workflow: audit → fix → polish
+/critique /harden checkout        # UX review + add error handling
+```
+
+### Anti-Patterns
+
+The skill includes explicit guidance on what to avoid:
+
+- Don't use overused fonts (Arial, Inter, system defaults)
+- Don't use gray text on colored backgrounds
+- Don't use pure black/gray (always tint)
+- Don't wrap everything in cards or nest cards inside cards
+- Don't use bounce/elastic easing (feels dated)
+
+## See It In Action
+
+Visit [impeccable.style](https://impeccable.style#casestudies) to see before/after case studies of real projects transformed with Impeccable commands.
+
+## Installation
+
+### Option 1: Download from Website (Recommended)
+
+Visit [impeccable.style](https://impeccable.style), download the ZIP for your tool, and extract to your project.
+
+### Option 2: Copy from Repository
+
+**Cursor:**
+```bash
+cp -r dist/cursor/.cursor your-project/
+```
+
+> **Note:** Cursor skills require setup:
+> 1. Switch to Nightly channel in Cursor Settings → Beta
+> 2. Enable Agent Skills in Cursor Settings → Rules
+>
+> [Learn more about Cursor skills](https://cursor.com/docs/context/skills)
+
+**Claude Code:**
+```bash
+# Project-specific
+cp -r dist/claude-code/.claude your-project/
+
+# Or global (applies to all projects)
+cp -r dist/claude-code/.claude/* ~/.claude/
+```
+
+**OpenCode:**
+```bash
+cp -r dist/opencode/.opencode your-project/
+```
+
+**Pi:**
+```bash
+cp -r dist/pi/.pi your-project/
+```
+
+**Gemini CLI:**
+```bash
+cp -r dist/gemini/.gemini your-project/
+```
+
+> **Note:** Gemini CLI skills require setup:
+> 1. Install preview version: `npm i -g @google/gemini-cli@preview`
+> 2. Run `/settings` and enable "Skills"
+> 3. Run `/skills list` to verify installation
+>
+> [Learn more about Gemini CLI skills](https://geminicli.com/docs/cli/skills/)
+
+**Codex CLI:**
+```bash
+cp -r dist/codex/.codex/* ~/.codex/
+```
+
+**Trae:**
+```bash
+# Trae China (domestic version)
+cp -r dist/trae/.trae-cn/skills/* ~/.trae-cn/skills/
+
+# Trae International
+cp -r dist/trae/.trae/skills/* ~/.trae/skills/
+```
+
+> **Note:** Trae has two versions with different config directories:
+> - **Trae China**: `~/.trae-cn/skills/`
+> - **Trae International**: `~/.trae/skills/`
+>
+> After copying, restart Trae IDE to activate the skills.
+
+**Rovo Dev:**
+```bash
+# Project-specific
+cp -r dist/rovo-dev/.rovodev your-project/
+
+# Or global (applies to all projects)
+cp -r dist/rovo-dev/.rovodev/skills/* ~/.rovodev/skills/
+```
+
+## Usage
+
+Once installed, use commands in your AI harness:
+
+```
+/audit           # Find issues
+/normalize       # Fix inconsistencies
+/polish          # Final cleanup
+/distill         # Remove complexity
+```
+
+Most commands accept an optional argument to focus on a specific area:
+
+```
+/audit header
+/polish checkout-form
+```
+
+**Note:** Codex CLI uses a different syntax: `/prompts:audit`, `/prompts:polish`, etc.
+
+## CLI
+
+Impeccable includes a standalone CLI for detecting anti-patterns without an AI harness:
+
+```bash
+npx impeccable detect src/                   # scan a directory
+npx impeccable detect index.html             # scan an HTML file
+npx impeccable detect https://example.com    # scan a URL (Puppeteer)
+npx impeccable detect --fast --json .        # regex-only, JSON output
+```
+
+The detector catches 24 issues across AI slop (side-tab borders, purple gradients, bounce easing, dark glows) and general design quality (line length, cramped padding, small touch targets, skipped headings, and more).
+
+## Supported Tools
+
+- [Cursor](https://cursor.com)
+- [Claude Code](https://claude.ai/code)
+- [OpenCode](https://opencode.ai)
+- [Pi](https://pi.dev)
+- [Gemini CLI](https://github.com/google-gemini/gemini-cli)
+- [Codex CLI](https://github.com/openai/codex)
+- [VS Code Copilot](https://code.visualstudio.com)
+- [Kiro](https://kiro.dev)
+- [Trae](https://trae.ai)
+- [Rovo Dev](https://www.atlassian.com/software/rovo)
+
+## Contributing
+
+See [DEVELOP.md](DEVELOP.md) for contributor guidelines and build instructions.
+
+## License
+
+Apache 2.0. See [LICENSE](LICENSE).
+
+The impeccable skill builds on [Anthropic's original frontend-design skill](https://github.com/anthropics/skills/tree/main/skills/frontend-design). See [NOTICE.md](NOTICE.md) for attribution.
+
+---
+
+Created by [Paul Bakaus](https://www.paulbakaus.com)

README.npm.md 🔗

@@ -0,0 +1,76 @@
+# Impeccable CLI
+
+Detect UI anti-patterns and design quality issues from the command line. Scans HTML, CSS, JSX, TSX, Vue, and Svelte files for 25 specific patterns including AI-generated UI tells, accessibility violations, and general design quality problems.
+
+## Quick Start
+
+```bash
+# Install skills into your AI harness (Claude, Cursor, Gemini, etc.)
+npx impeccable skills install
+
+# Update skills to the latest version
+npx impeccable skills update
+
+# List all available commands
+npx impeccable skills help
+
+# Scan files or directories for anti-patterns
+npx impeccable detect src/
+
+# Scan a live URL (requires Puppeteer)
+npx impeccable detect https://example.com
+
+# JSON output for CI/tooling
+npx impeccable detect --json src/
+
+# Regex-only mode (faster, no jsdom)
+npx impeccable detect --fast src/
+```
+
+## What It Detects
+
+**AI Slop Tells**: patterns that scream "AI generated this":
+- Side-tab accent borders, gradient text on headings
+- Purple/violet gradients and cyan-on-dark palettes
+- Dark mode with glowing accents, border + border-radius clashes
+
+**Typography Issues**: overused fonts (Inter, Roboto), flat type hierarchy, single font families
+
+**Color & Contrast**: WCAG AA violations, gray text on colored backgrounds, pure black/white
+
+**Layout & Composition**: nested cards, monotonous spacing, everything-centered layouts
+
+**Motion**: bounce/elastic easing, layout property transitions
+
+**Quality**: tiny body text, cramped padding, long line lengths, small touch targets
+
+25 detections in total. See the full list at [impeccable.style](https://impeccable.style).
+
+## Exit Codes
+
+- `0`: no issues found
+- `2`: anti-patterns detected
+
+## Options
+
+```
+impeccable detect [options] [file-or-dir-or-url...]
+
+  --fast    Regex-only mode (skip jsdom, faster but less accurate)
+  --json    Output findings as JSON
+  --help    Show help
+```
+
+## Requirements
+
+- Node.js 18+
+- `jsdom` (included as dependency, used for HTML scanning)
+- `puppeteer` (optional, only needed for URL scanning)
+
+## Part of Impeccable
+
+This CLI is part of [Impeccable](https://impeccable.style), a cross-provider design skill pack for AI-powered development tools. The full suite includes 22 steering commands for Claude, Cursor, Gemini, Codex, and more.
+
+## License
+
+[Apache 2.0](https://github.com/pbakaus/impeccable/blob/main/LICENSE)

bin/cli.js 🔗

@@ -0,0 +1,64 @@
+#!/usr/bin/env node
+
+/**
+ * Impeccable CLI
+ *
+ * Usage:
+ *   npx impeccable detect [file-or-dir-or-url...]
+ *   npx impeccable live [--port=PORT]
+ *   npx impeccable live stop
+ *   npx impeccable skills help|install|update
+ *   npx impeccable --help
+ */
+
+import { readFileSync } from 'node:fs';
+import { join, dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const args = process.argv.slice(2);
+const command = args[0];
+
+if (!command || command === '--help' || command === '-h') {
+  console.log(`Usage: impeccable <command> [options]
+
+Commands:
+  detect [file-or-dir-or-url...]   Scan for UI anti-patterns and design quality issues
+  live [--port=PORT]               Start browser detection overlay server
+  live stop                        Stop a running live server
+  skills help                      List all available skills and commands
+  skills install                   Install impeccable skills into your project
+  skills update                    Update skills to the latest version
+  skills check                     Check if skill updates are available
+
+Options:
+  --help       Show this help message
+  --version    Show version number
+
+Run 'impeccable <command> --help' for command-specific options.`);
+  process.exit(0);
+}
+
+if (command === '--version' || command === '-v') {
+  const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
+  console.log(pkg.version);
+  process.exit(0);
+}
+
+if (command === 'detect') {
+  process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
+  const { detectCli } = await import('../src/detect-antipatterns.mjs');
+  await detectCli();
+} else if (command === 'live') {
+  process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
+  const { liveCli } = await import('../src/detect-antipatterns.mjs');
+  await liveCli();
+} else if (command === 'skills') {
+  const { run } = await import('./commands/skills.mjs');
+  await run(args.slice(1));
+} else {
+  // Default: treat as detect arguments (allow `npx impeccable src/` shorthand)
+  process.argv = [process.argv[0], process.argv[1], ...args];
+  const { detectCli } = await import('../src/detect-antipatterns.mjs');
+  await detectCli();
+}

bin/commands/skills.mjs 🔗

@@ -0,0 +1,648 @@
+/**
+ * `impeccable skills` subcommand
+ *
+ * Usage:
+ *   impeccable skills help      Show all available skills and commands
+ *   impeccable skills install   Install skills via npx skills add
+ *   impeccable skills update    Update skills to latest version
+ */
+
+import { execSync } from 'node:child_process';
+import { existsSync, readFileSync, readdirSync, statSync, lstatSync, symlinkSync, readlinkSync, unlinkSync, mkdirSync, writeFileSync, rmSync, renameSync, createWriteStream, realpathSync } from 'node:fs';
+import { join, resolve, dirname } from 'node:path';
+import { createInterface } from 'node:readline';
+import { fileURLToPath } from 'node:url';
+import { get } from 'node:https';
+import { createHash } from 'node:crypto';
+import { tmpdir } from 'node:os';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const API_BASE = 'https://impeccable.style';
+
+// Provider folder names in project roots
+const PROVIDER_DIRS = ['.claude', '.cursor', '.gemini', '.codex', '.agents', '.kiro', '.opencode', '.pi', '.trae', '.trae-cn'];
+
+function ask(question) {
+  const rl = createInterface({ input: process.stdin, output: process.stdout });
+  return new Promise(r => rl.question(question, ans => { rl.close(); r(ans.trim().toLowerCase()); }));
+}
+
+// ─── skills help ──────────────────────────────────────────────────────────────
+
+async function showHelp() {
+  let commands;
+  try {
+    const res = await fetch(`${API_BASE}/api/commands`);
+    commands = await res.json();
+  } catch {
+    console.error('Could not fetch command list from impeccable.style. Check your network connection.');
+    process.exit(1);
+  }
+
+  const pad = (s, n) => s + ' '.repeat(Math.max(0, n - s.length));
+
+  console.log('\n  Impeccable Skills & Commands\n');
+  console.log('  Install:  npx impeccable skills install');
+  console.log('  Update:   npx impeccable skills update');
+  console.log('  Docs:     https://impeccable.style/cheatsheet\n');
+  console.log(`  ${pad('Command', 22)} Description`);
+  console.log(`  ${'-'.repeat(22)} ${'-'.repeat(52)}`);
+
+  for (const cmd of commands.sort((a, b) => a.id.localeCompare(b.id))) {
+    // Trim description to fit terminal
+    const desc = cmd.description.length > 72
+      ? cmd.description.substring(0, 69) + '...'
+      : cmd.description;
+    console.log(`  ${pad('/' + cmd.id, 22)} ${desc}`);
+  }
+  console.log(`\n  ${commands.length} commands available. Run /<command> in your AI harness.\n`);
+}
+
+// ─── version helpers ─────────────────────────────────────────────────────────
+
+/**
+ * Read the skills version from the impeccable SKILL.md frontmatter.
+ */
+function getSkillsVersion(root) {
+  for (const d of PROVIDER_DIRS) {
+    const skillMd = join(root, d, 'skills', 'impeccable', 'SKILL.md');
+    if (!existsSync(skillMd)) continue;
+    const content = readFileSync(skillMd, 'utf-8');
+    const match = content.match(/^version:\s*(.+)$/m);
+    if (match) return match[1].trim().replace(/^["']|["']$/g, '');
+  }
+  return null;
+}
+
+/**
+ * Hash all SKILL.md files in a directory tree for comparison.
+ * Returns a sorted string of "name:hash" pairs.
+ */
+function hashSkillsDir(skillsDir) {
+  if (!existsSync(skillsDir)) return '';
+  const entries = [];
+  for (const name of readdirSync(skillsDir).sort()) {
+    const skillMd = join(skillsDir, name, 'SKILL.md');
+    if (!existsSync(skillMd)) continue;
+    const hash = createHash('sha256').update(readFileSync(skillMd)).digest('hex').slice(0, 12);
+    entries.push(`${name}:${hash}`);
+  }
+  return entries.join(',');
+}
+
+/**
+ * Download the universal bundle to a temp dir and return its path.
+ * Caller is responsible for cleanup.
+ */
+async function downloadAndExtractBundle() {
+  const tmpZip = join(tmpdir(), `impeccable-update-${Date.now()}.zip`);
+  const tmpDir = join(tmpdir(), `impeccable-update-${Date.now()}`);
+  await downloadFile(`${API_BASE}/api/download/bundle/universal`, tmpZip);
+  mkdirSync(tmpDir, { recursive: true });
+  execSync(`unzip -qo "${tmpZip}" -d "${tmpDir}"`, { encoding: 'utf8' });
+  rmSync(tmpZip, { force: true });
+  return tmpDir;
+}
+
+/**
+ * Normalize a SKILL.md's content for comparison by stripping
+ * provider-specific paths. Different install methods (npx skills add
+ * vs our bundle) resolve {{scripts_path}} to different provider dirs
+ * (e.g. .agents vs .claude), so we strip those differences.
+ */
+function normalizeForHash(content) {
+  return content
+    .replace(/\.(claude|cursor|agents|gemini|codex|kiro|opencode|pi|trae|trae-cn|rovodev)\/skills\//g, '.PROVIDER/skills/')
+    .replace(/^version:\s*.+$/m, 'version: NORMALIZED');
+}
+
+/**
+ * Deduplicate providers by resolved path. When .claude/skills is a
+ * symlink to ../.agents/skills, both resolve to the same directory.
+ * Returns an array of { provider, localSkillsDir } with one entry
+ * per unique real path. The first provider that maps to a real path
+ * wins (so the bundle uses that provider's build).
+ */
+function deduplicateProviders(root, providers) {
+  const seen = new Map(); // realPath -> { provider, localSkillsDir }
+  for (const provider of providers) {
+    const skillsDir = join(root, provider, 'skills');
+    if (!existsSync(skillsDir)) continue;
+    const real = realpathSync(skillsDir);
+    if (!seen.has(real)) {
+      seen.set(real, { provider, localSkillsDir: skillsDir });
+    }
+  }
+  return [...seen.values()];
+}
+
+/**
+ * Compare local skills against a downloaded bundle.
+ * Only checks skills that exist in the bundle (ignores user's custom
+ * skills that aren't part of impeccable). Deduplicates providers that
+ * share the same real path (symlinks). Normalizes provider-specific
+ * paths and version fields before comparing.
+ * Returns true if every bundle skill matches the local copy.
+ */
+function isUpToDate(root, providers, bundleDir) {
+  const unique = deduplicateProviders(root, providers);
+  if (unique.length === 0) return false;
+
+  for (const { provider, localSkillsDir } of unique) {
+    const bundleSkillsDir = join(bundleDir, provider, 'skills');
+    if (!existsSync(bundleSkillsDir)) continue;
+
+    for (const name of readdirSync(bundleSkillsDir)) {
+      const bundleMd = join(bundleSkillsDir, name, 'SKILL.md');
+      const localMd = join(localSkillsDir, name, 'SKILL.md');
+      if (!existsSync(bundleMd)) continue;
+      if (!existsSync(localMd)) return false;
+
+      const bundleHash = createHash('sha256').update(normalizeForHash(readFileSync(bundleMd, 'utf-8'))).digest('hex');
+      const localHash = createHash('sha256').update(normalizeForHash(readFileSync(localMd, 'utf-8'))).digest('hex');
+      if (bundleHash !== localHash) return false;
+    }
+  }
+  return true;
+}
+
+// ─── skills check ────────────────────────────────────────────────────────────
+
+async function check() {
+  const root = findProjectRoot();
+  const installed = isAlreadyInstalled(root);
+
+  if (!installed) {
+    console.log('Impeccable is not installed in this project.');
+    console.log('Run `npx impeccable skills install` to install.');
+    process.exit(0);
+  }
+
+  const providers = findInstalledProviders(root);
+
+  console.log('Checking for updates...\n');
+  try {
+    const bundleDir = await downloadAndExtractBundle();
+    const upToDate = isUpToDate(root, providers, bundleDir);
+    rmSync(bundleDir, { recursive: true, force: true });
+
+    if (upToDate) {
+      const v = getSkillsVersion(root);
+      console.log(`Skills are up to date${v ? ` (v${v})` : ''}.`);
+    } else {
+      console.log('Updates available.');
+      console.log('Run `npx impeccable skills update` to update.');
+    }
+  } catch (e) {
+    console.error(`Could not check for updates: ${e.message}`);
+    process.exit(1);
+  }
+}
+
+// ─── skills install ───────────────────────────────────────────────────────────
+
+// Check if impeccable skills are already present in any provider folder
+function isAlreadyInstalled(root) {
+  for (const d of PROVIDER_DIRS) {
+    const skillsDir = join(root, d, 'skills');
+    if (!existsSync(skillsDir)) continue;
+    try {
+      const entries = readdirSync(skillsDir);
+      // Look for 'impeccable' skill (or prefixed variant, or legacy 'teach-impeccable')
+      if (entries.some(e =>
+        e === 'impeccable' || e.endsWith('-impeccable') ||
+        e === 'teach-impeccable' || e.endsWith('-teach-impeccable')
+      )) {
+        return d;
+      }
+    } catch {}
+  }
+  return null;
+}
+
+function escapeRegex(str) {
+  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
+function prefixSkillContent(content, prefix, allSkillNames) {
+  // Prefix the name in frontmatter
+  let result = content.replace(/^name:\s*(.+)$/m, (_, name) => `name: ${prefix}${name.trim()}`);
+
+  // Prefix cross-references: /skillname -> /prefix-skillname
+  const sorted = [...allSkillNames].sort((a, b) => b.length - a.length);
+  for (const name of sorted) {
+    // Command invocations: /skillname
+    result = result.replace(
+      new RegExp(`/(?=${escapeRegex(name)}(?:[^a-zA-Z0-9_-]|$))`, 'g'),
+      `/${prefix}`
+    );
+    // Prose references: "the skillname skill"
+    result = result.replace(
+      new RegExp(`(the) ${escapeRegex(name)} skill`, 'gi'),
+      (_, article) => `${article} ${prefix}${name} skill`
+    );
+  }
+  return result;
+}
+
+function isSkillDir(skillsDir, name) {
+  // Skill entries can be real directories or symlinks to directories (npx skills uses symlinks)
+  const full = join(skillsDir, name);
+  try {
+    return statSync(full).isDirectory() && existsSync(join(full, 'SKILL.md'));
+  } catch { return false; }
+}
+
+function isRealSkillDir(skillsDir, name) {
+  // Only real directories, not symlinks -- renaming the real dir renames the symlink targets too
+  const full = join(skillsDir, name);
+  try {
+    const lstat = lstatSync(full);
+    return lstat.isDirectory() && !lstat.isSymbolicLink() && existsSync(join(full, 'SKILL.md'));
+  } catch { return false; }
+}
+
+function renameSkillsWithPrefix(root, prefix) {
+  // First pass: collect all skill names across all providers (use first provider found)
+  let allSkillNames = [];
+  for (const d of PROVIDER_DIRS) {
+    const skillsDir = join(root, d, 'skills');
+    if (!existsSync(skillsDir)) continue;
+    const entries = readdirSync(skillsDir);
+    allSkillNames = entries.filter(name => isSkillDir(skillsDir, name));
+    if (allSkillNames.length > 0) break;
+  }
+
+  // Second pass: rename real dirs and update their content
+  let count = 0;
+  for (const d of PROVIDER_DIRS) {
+    const skillsDir = join(root, d, 'skills');
+    if (!existsSync(skillsDir)) continue;
+    try {
+      const entries = readdirSync(skillsDir);
+      for (const name of entries) {
+        if (name.startsWith(prefix)) continue;
+        if (!isRealSkillDir(skillsDir, name)) continue;
+
+        const src = join(skillsDir, name);
+        const dest = join(skillsDir, prefix + name);
+
+        renameSync(src, dest);
+
+        // Prefix frontmatter name + all cross-references in SKILL.md
+        let content = readFileSync(join(dest, 'SKILL.md'), 'utf8');
+        content = prefixSkillContent(content, prefix, allSkillNames);
+        writeFileSync(join(dest, 'SKILL.md'), content);
+        count++;
+      }
+    } catch {}
+  }
+
+  // Third pass: fix symlinks that now point to renamed targets (npx skills uses these)
+  for (const d of PROVIDER_DIRS) {
+    const skillsDir = join(root, d, 'skills');
+    if (!existsSync(skillsDir)) continue;
+    try {
+      const entries = readdirSync(skillsDir);
+      for (const name of entries) {
+        if (name.startsWith(prefix)) continue;
+        const full = join(skillsDir, name);
+        try {
+          if (!lstatSync(full).isSymbolicLink()) continue;
+          const target = readlinkSync(full);
+          const newTarget = target.replace(new RegExp(`/${escapeRegex(name)}$`), `/${prefix}${name}`);
+          unlinkSync(full);
+          symlinkSync(newTarget, join(skillsDir, prefix + name));
+        } catch {}
+      }
+    } catch {}
+  }
+
+  return count;
+}
+
+async function install(flags) {
+  const force = flags.includes('--force');
+  const yes = flags.includes('-y') || flags.includes('--yes');
+  const prefixFlag = flags.find(f => f.startsWith('--prefix='));
+  const root = findProjectRoot();
+  const existing = isAlreadyInstalled(root);
+
+  if (existing && !force) {
+    console.log(`Impeccable skills are already installed (found in ${existing}/).`);
+    console.log('Run with --force to reinstall.\n');
+    process.exit(0);
+  }
+
+  console.log('Installing impeccable skills via npx skills...\n');
+  try {
+    execSync(`npx skills add pbakaus/impeccable${yes ? ' -y' : ''}`, { stdio: 'inherit' });
+  } catch (e) {
+    process.exit(e.status ?? 1);
+  }
+
+  // Ask about prefixing (skip in CI mode unless --prefix= is set)
+  let prefix = '';
+  if (prefixFlag) {
+    prefix = prefixFlag.split('=')[1] || 'i-';
+  } else if (!yes) {
+    console.log();
+    const wantPrefix = await ask('Prefix commands to avoid conflicts? e.g. /i-audit instead of /audit (y/N) ');
+    if (wantPrefix === 'y' || wantPrefix === 'yes') {
+      const custom = await ask('Prefix (default: i-): ');
+      prefix = custom || 'i-';
+    }
+  }
+
+  if (prefix) {
+    const count = renameSkillsWithPrefix(root, prefix);
+    if (count > 0) {
+      console.log(`\nRenamed ${count} skills with "${prefix}" prefix.`);
+      console.log(`Commands are now available as /${prefix}<command> (e.g. /${prefix}audit).`);
+    }
+  }
+
+  // Clean up deprecated skills from previous versions
+  try {
+    const { cleanup } = await import('../../source/skills/impeccable/scripts/cleanup-deprecated.mjs');
+    const result = cleanup(root);
+    const total = result.deletedPaths.length + result.removedLockEntries.length;
+    if (total > 0) {
+      console.log(`Cleaned up ${total} deprecated skill(s) from previous versions.`);
+    }
+  } catch {
+    // Cleanup script not available -- skip
+  }
+
+  console.log(`\nDone! Run /${prefix}impeccable teach in your AI harness to set up design context.\n`);
+}
+
+/** Detect prefix by looking for the 'impeccable' skill (or legacy 'teach-impeccable') */
+function detectPrefix(root) {
+  for (const d of PROVIDER_DIRS) {
+    const skillsDir = join(root, d, 'skills');
+    if (!existsSync(skillsDir)) continue;
+    for (const name of readdirSync(skillsDir)) {
+      if (name === 'impeccable') return '';
+      if (name.endsWith('-impeccable') && name !== 'teach-impeccable') return name.slice(0, -'impeccable'.length);
+      // Legacy fallback
+      if (name === 'teach-impeccable') return '';
+      if (name.endsWith('-teach-impeccable')) return name.slice(0, -'teach-impeccable'.length);
+    }
+  }
+  return '';
+}
+
+/** Undo prefixing: rename folders back and strip prefix from SKILL.md content */
+function undoPrefix(root, prefix) {
+  if (!prefix) return;
+  // Collect the unprefixed names (strip our prefix)
+  let allPrefixedNames = [];
+  for (const d of PROVIDER_DIRS) {
+    const skillsDir = join(root, d, 'skills');
+    if (!existsSync(skillsDir)) continue;
+    allPrefixedNames = readdirSync(skillsDir).filter(n => n.startsWith(prefix) && isRealSkillDir(skillsDir, n));
+    if (allPrefixedNames.length > 0) break;
+  }
+  const unprefixedNames = allPrefixedNames.map(n => n.slice(prefix.length));
+
+  for (const d of PROVIDER_DIRS) {
+    const skillsDir = join(root, d, 'skills');
+    if (!existsSync(skillsDir)) continue;
+    for (const name of readdirSync(skillsDir)) {
+      if (!name.startsWith(prefix)) continue;
+      const unprefixed = name.slice(prefix.length);
+      const src = join(skillsDir, name);
+      const dest = join(skillsDir, unprefixed);
+
+      if (lstatSync(src).isSymbolicLink()) {
+        const target = readlinkSync(src);
+        const newTarget = target.replace(`/${name}`, `/${unprefixed}`);
+        unlinkSync(src);
+        symlinkSync(newTarget, dest);
+      } else {
+        renameSync(src, dest);
+        // Strip prefix from SKILL.md content
+        const skillMd = join(dest, 'SKILL.md');
+        if (existsSync(skillMd)) {
+          let content = readFileSync(skillMd, 'utf8');
+          // Reverse the prefixing: replace prefixed names with unprefixed
+          content = content.replace(new RegExp(`^name:\\s*${escapeRegex(prefix)}`, 'm'), 'name: ');
+          const sorted = [...allPrefixedNames].sort((a, b) => b.length - a.length);
+          for (const pName of sorted) {
+            const uName = pName.slice(prefix.length);
+            content = content.replace(new RegExp(`/${escapeRegex(pName)}(?=[^a-zA-Z0-9_-]|$)`, 'g'), `/${uName}`);
+            content = content.replace(new RegExp(`(the) ${escapeRegex(pName)} skill`, 'gi'), `$1 ${uName} skill`);
+          }
+          writeFileSync(skillMd, content);
+        }
+      }
+    }
+  }
+}
+
+// ─── skills update ────────────────────────────────────────────────────────────
+
+function findProjectRoot() {
+  let dir = process.cwd();
+  while (dir !== dirname(dir)) {
+    if (existsSync(join(dir, '.git'))) return dir;
+    dir = dirname(dir);
+  }
+  return process.cwd();
+}
+
+function findInstalledProviders(root) {
+  const found = [];
+  for (const d of PROVIDER_DIRS) {
+    const skillsDir = join(root, d, 'skills');
+    if (!existsSync(skillsDir)) continue;
+    try {
+      const entries = readdirSync(skillsDir);
+      if (entries.some(name => isSkillDir(skillsDir, name))) found.push(d);
+    } catch {}
+  }
+  return found;
+}
+
+function getModifiedSkillFiles(root, providerDirs) {
+  // Use git to check if any skill files have local modifications
+  const modified = [];
+  try {
+    const status = execSync('git status --porcelain', { cwd: root, encoding: 'utf8' });
+    for (const line of status.split('\n')) {
+      if (!line.trim()) continue;
+      const file = line.substring(3);
+      for (const d of providerDirs) {
+        if (file.startsWith(`${d}/skills/`)) {
+          const flag = line.substring(0, 2).trim();
+          modified.push({ file, flag });
+        }
+      }
+    }
+  } catch {
+    // Not a git repo or git not available
+  }
+  return modified;
+}
+
+function downloadFile(url, dest) {
+  return new Promise((resolve, reject) => {
+    const file = createWriteStream(dest);
+    get(url, (res) => {
+      if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
+        // Follow redirect
+        get(res.headers.location, (res2) => {
+          res2.pipe(file);
+          file.on('finish', () => { file.close(); resolve(); });
+        }).on('error', reject);
+        return;
+      }
+      if (res.statusCode !== 200) {
+        reject(new Error(`HTTP ${res.statusCode}`));
+        return;
+      }
+      res.pipe(file);
+      file.on('finish', () => { file.close(); resolve(); });
+    }).on('error', reject);
+  });
+}
+
+async function update(flags = []) {
+  const yes = flags.includes('-y') || flags.includes('--yes');
+
+  // Clean up deprecated skills from previous versions.
+  try {
+    const { cleanup } = await import('../../source/skills/impeccable/scripts/cleanup-deprecated.mjs');
+    const root = findProjectRoot();
+    const result = cleanup(root);
+    const total = result.deletedPaths.length + result.removedLockEntries.length;
+    if (total > 0) {
+      console.log(`Cleaned up ${total} deprecated skill(s) from previous versions.\n`);
+    }
+  } catch {
+    // Cleanup script not available (e.g. running from npm package) -- skip
+  }
+
+  // Download the latest skills directly from impeccable.style.
+  // We skip `npx skills update` because it has a known upstream bug
+  // (vercel-labs/skills#775) where it can't find the lock file.
+  const root = findProjectRoot();
+  const providers = findInstalledProviders(root);
+
+  if (providers.length === 0) {
+    console.log('No impeccable skill folders found in this project.');
+    console.log('Run `npx impeccable skills install` to install first.');
+    process.exit(1);
+  }
+
+  console.log('Checking for updates...');
+
+  let tmpDir;
+  try {
+    tmpDir = await downloadAndExtractBundle();
+  } catch (e) {
+    console.error(`Download failed: ${e.message}`);
+    process.exit(1);
+  }
+
+  // Compare local vs remote -- skip if already up to date
+  if (isUpToDate(root, providers, tmpDir)) {
+    rmSync(tmpDir, { recursive: true, force: true });
+    const v = getSkillsVersion(root);
+    console.log(`Skills are up to date${v ? ` (v${v})` : ''}. Nothing to do.`);
+    process.exit(0);
+  }
+
+  console.log(`Found skills in: ${providers.join(', ')}`);
+
+  if (!yes) {
+    const ans = await ask(`Update skills in ${providers.length} provider folder(s)? (Y/n) `);
+    if (ans === 'n' || ans === 'no') {
+      rmSync(tmpDir, { recursive: true, force: true });
+      console.log('Aborted.');
+      process.exit(0);
+    }
+  }
+
+  try {
+
+    // Copy from the bundle to each unique provider folder.
+    // Deduplicate so symlinked dirs (e.g. .claude/skills -> .agents/skills)
+    // are only written once with the correct provider's content.
+    const unique = deduplicateProviders(root, providers);
+    let updated = 0;
+    for (const { provider, localSkillsDir } of unique) {
+      const srcDir = join(tmpDir, provider, 'skills');
+      if (!existsSync(srcDir)) continue;
+
+      const skills = readdirSync(srcDir, { withFileTypes: true });
+      for (const skill of skills) {
+        if (!skill.isDirectory()) continue;
+        const src = join(srcDir, skill.name);
+        const dest = join(localSkillsDir, skill.name);
+        if (existsSync(dest)) rmSync(dest, { recursive: true });
+        copyDirSync(src, dest);
+        updated++;
+      }
+    }
+
+    rmSync(tmpDir, { recursive: true, force: true });
+
+    // Re-apply prefix if detected
+    const prefix = detectPrefix(root);
+    if (prefix) {
+      const count = renameSkillsWithPrefix(root, prefix);
+      if (count > 0) console.log(`Re-applied "${prefix}" prefix to ${count} skills.`);
+    }
+
+    // Run cleanup to remove deprecated stubs from the fresh download
+    try {
+      const { cleanup: postCleanup } = await import('../../source/skills/impeccable/scripts/cleanup-deprecated.mjs');
+      postCleanup(root);
+    } catch {
+      // Not available -- skip
+    }
+
+    const v = getSkillsVersion(root);
+    console.log(`Updated ${updated} skill(s)${v ? ` to v${v}` : ''}.`);
+    console.log('Done!\n');
+  } catch (e) {
+    console.error(`Update failed: ${e.message}`);
+    if (tmpDir) rmSync(tmpDir, { recursive: true, force: true });
+    process.exit(1);
+  }
+}
+
+function copyDirSync(src, dest) {
+  mkdirSync(dest, { recursive: true });
+  for (const entry of readdirSync(src, { withFileTypes: true })) {
+    const s = join(src, entry.name);
+    const d = join(dest, entry.name);
+    if (entry.isDirectory()) {
+      copyDirSync(s, d);
+    } else {
+      writeFileSync(d, readFileSync(s));
+    }
+  }
+}
+
+// ─── Router ───────────────────────────────────────────────────────────────────
+
+export async function run(args) {
+  const sub = args[0];
+
+  if (!sub || sub === 'help' || sub === '--help' || sub === '-h') {
+    await showHelp();
+  } else if (sub === 'install') {
+    await install(args.slice(1));
+  } else if (sub === 'update') {
+    await update(args.slice(1));
+  } else if (sub === 'check') {
+    await check();
+  } else {
+    console.error(`Unknown skills command: ${sub}`);
+    console.error(`Run 'impeccable skills --help' for available commands.`);
+    process.exit(1);
+  }
+}

biome.json 🔗

@@ -0,0 +1,12 @@
+{
+  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
+  "css": {
+    "parser": {
+      "tailwindDirectives": true
+    }
+  }
+}
+
+
+
+

bun.lock 🔗

@@ -0,0 +1,607 @@
+{
+  "lockfileVersion": 1,
+  "configVersion": 1,
+  "workspaces": {
+    "": {
+      "name": "vibe-design-plugins",
+      "dependencies": {
+        "jsdom": "^29.0.0",
+        "marked": "^16.1.0",
+      },
+      "devDependencies": {
+        "archiver": "^7.0.1",
+        "motion": "^12.38.0",
+        "playwright": "^1.58.2",
+        "wrangler": "^4.75.0",
+      },
+      "optionalDependencies": {
+        "puppeteer": "^24.39.1",
+      },
+    },
+  },
+  "packages": {
+    "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "lru-cache": "^11.2.6" } }, "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw=="],
+
+    "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@7.0.3", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.2.7" } }, "sha512-Q6mU0Z6bfj6YvnX2k9n0JxiIwrCFN59x/nWmYQnAqP000ruX/yV+5bp/GRcF5T8ncvfwJQ7fgfP74DlpKExILA=="],
+
+    "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="],
+
+    "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
+
+    "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
+
+    "@bramus/specificity": ["@bramus/specificity@2.4.2", "", { "dependencies": { "css-tree": "^3.0.0" }, "bin": { "specificity": "bin/cli.js" } }, "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw=="],
+
+    "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="],
+
+    "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.15.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-EGYmJaGZKWl+X8tXxcnx4v2bOZSjQeNI5dWFeXivgX9+YCT69AkzHHwlNbVpqtEUTbew8eQurpyOpeN8fg00nw=="],
+
+    "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260317.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-8hjh3sPMwY8M/zedq3/sXoA2Q4BedlGufn3KOOleIG+5a4ReQKLlUah140D7J6zlKmYZAFMJ4tWC7hCuI/s79g=="],
+
+    "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260317.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-M/MnNyvO5HMgoIdr3QHjdCj2T1ki9gt0vIUnxYxBu9ISXS/jgtMl6chUVPJ7zHYBn9MyYr8ByeN6frjYxj0MGg=="],
+
+    "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260317.1", "", { "os": "linux", "cpu": "x64" }, "sha512-1ltuEjkRcS3fsVF7CxsKlWiRmzq2ZqMfqDN0qUOgbUwkpXsLVJsXmoblaLf5OP00ELlcgF0QsN0p2xPEua4Uug=="],
+
+    "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260317.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-3QrNnPF1xlaNwkHpasvRvAMidOvQs2NhXQmALJrEfpIJ/IDL2la8g499yXp3eqhG3hVMCB07XVY149GTs42Xtw=="],
+
+    "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260317.1", "", { "os": "win32", "cpu": "x64" }, "sha512-MfZTz+7LfuIpMGTa3RLXHX8Z/pnycZLItn94WRdHr8LPVet+C5/1Nzei399w/jr3+kzT4pDKk26JF/tlI5elpQ=="],
+
+    "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
+
+    "@csstools/color-helpers": ["@csstools/color-helpers@6.0.2", "", {}, "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q=="],
+
+    "@csstools/css-calc": ["@csstools/css-calc@3.1.1", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ=="],
+
+    "@csstools/css-color-parser": ["@csstools/css-color-parser@4.0.2", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.1.1" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw=="],
+
+    "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@4.0.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w=="],
+
+    "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.1.1", "", { "peerDependencies": { "css-tree": "^3.2.1" }, "optionalPeers": ["css-tree"] }, "sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w=="],
+
+    "@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="],
+
+    "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
+
+    "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
+
+    "@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
+
+    "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="],
+
+    "@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="],
+
+    "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="],
+
+    "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="],
+
+    "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="],
+
+    "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="],
+
+    "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="],
+
+    "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="],
+
+    "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="],
+
+    "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="],
+
+    "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="],
+
+    "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="],
+
+    "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="],
+
+    "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="],
+
+    "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="],
+
+    "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="],
+
+    "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="],
+
+    "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="],
+
+    "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="],
+
+    "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="],
+
+    "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="],
+
+    "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="],
+
+    "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="],
+
+    "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="],
+
+    "@exodus/bytes": ["@exodus/bytes@1.15.0", "", { "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" }, "optionalPeers": ["@noble/hashes"] }, "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ=="],
+
+    "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
+
+    "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
+
+    "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
+
+    "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
+
+    "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
+
+    "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
+
+    "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
+
+    "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
+
+    "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
+
+    "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
+
+    "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
+
+    "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
+
+    "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
+
+    "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
+
+    "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
+
+    "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
+
+    "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
+
+    "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
+
+    "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
+
+    "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
+
+    "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
+
+    "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
+
+    "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
+
+    "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
+
+    "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
+
+    "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
+
+    "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+
+    "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
+
+    "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="],
+
+    "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
+
+    "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="],
+
+    "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="],
+
+    "@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="],
+
+    "@puppeteer/browsers": ["@puppeteer/browsers@2.13.0", "", { "dependencies": { "debug": "^4.4.3", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.4", "tar-fs": "^3.1.1", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA=="],
+
+    "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="],
+
+    "@speed-highlight/core": ["@speed-highlight/core@1.2.14", "", {}, "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA=="],
+
+    "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="],
+
+    "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
+
+    "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
+
+    "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
+
+    "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
+
+    "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
+
+    "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+    "archiver": ["archiver@7.0.1", "", { "dependencies": { "archiver-utils": "^5.0.2", "async": "^3.2.4", "buffer-crc32": "^1.0.0", "readable-stream": "^4.0.0", "readdir-glob": "^1.1.2", "tar-stream": "^3.0.0", "zip-stream": "^6.0.1" } }, "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ=="],
+
+    "archiver-utils": ["archiver-utils@5.0.2", "", { "dependencies": { "glob": "^10.0.0", "graceful-fs": "^4.2.0", "is-stream": "^2.0.1", "lazystream": "^1.0.0", "lodash": "^4.17.15", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA=="],
+
+    "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
+    "ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="],
+
+    "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
+
+    "b4a": ["b4a@1.8.0", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg=="],
+
+    "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+    "bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="],
+
+    "bare-fs": ["bare-fs@4.5.5", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-XvwYM6VZqKoqDll8BmSww5luA5eflDzY0uEFfBJtFKe4PAAtxBjU3YIxzIBzhyaEQBy1VXEQBto4cpN5RZJw+w=="],
+
+    "bare-os": ["bare-os@3.7.1", "", {}, "sha512-ebvMaS5BgZKmJlvuWh14dg9rbUI84QeV3WlWn6Ph6lFI8jJoh7ADtVTyD2c93euwbe+zgi0DVrl4YmqXeM9aIA=="],
+
+    "bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="],
+
+    "bare-stream": ["bare-stream@2.8.1", "", { "dependencies": { "streamx": "^2.21.0", "teex": "^1.0.1" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer", "bare-events"] }, "sha512-bSeR8RfvbRwDpD7HWZvn8M3uYNDrk7m9DQjYOFkENZlXW8Ju/MPaqUPQq5LqJ3kyjEm07siTaAQ7wBKCU59oHg=="],
+
+    "bare-url": ["bare-url@2.3.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw=="],
+
+    "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
+
+    "basic-ftp": ["basic-ftp@5.2.0", "", {}, "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw=="],
+
+    "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="],
+
+    "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
+
+    "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
+
+    "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
+
+    "buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
+
+    "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
+
+    "chromium-bidi": ["chromium-bidi@14.0.0", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw=="],
+
+    "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
+
+    "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+    "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+    "compress-commons": ["compress-commons@6.0.2", "", { "dependencies": { "crc-32": "^1.2.0", "crc32-stream": "^6.0.0", "is-stream": "^2.0.1", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg=="],
+
+    "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
+
+    "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
+
+    "cosmiconfig": ["cosmiconfig@9.0.1", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ=="],
+
+    "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
+
+    "crc32-stream": ["crc32-stream@6.0.0", "", { "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^4.0.0" } }, "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g=="],
+
+    "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+
+    "css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="],
+
+    "data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="],
+
+    "data-urls": ["data-urls@7.0.0", "", { "dependencies": { "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0" } }, "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA=="],
+
+    "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+    "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="],
+
+    "degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="],
+
+    "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
+
+    "devtools-protocol": ["devtools-protocol@0.0.1581282", "", {}, "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ=="],
+
+    "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
+
+    "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
+
+    "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
+
+    "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
+
+    "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
+
+    "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="],
+
+    "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="],
+

content/site/anti-patterns-catalog.js 🔗

@@ -0,0 +1,353 @@
+/**
+ * Manual metadata for the /anti-patterns page.
+ *
+ * The detection rules themselves live in src/detect-antipatterns.mjs and
+ * are parsed at build time. This file adds three pieces of content that
+ * can't be automated:
+ *
+ *  1. DETECTION_LAYERS: which layer (cli, browser, or llm) catches the
+ *     rule. Manually classified by reading the detector source and the
+ *     browser-only test file.
+ *
+ *  2. VISUAL_EXAMPLES: a tiny inline HTML snippet showing what the
+ *     bad pattern actually looks like. Rendered inside each rule card.
+ *     Snippets should be self-contained with inline styles, use the
+ *     cream/paper/ink palette when possible, and sit naturally at
+ *     ~100% width by ~120px height.
+ *
+ *  3. LLM_ONLY_RULES: DON'T lines from source/skills/impeccable/SKILL.md
+ *     that do not map to any detection rule. These can only be caught by
+ *     the /critique skill's LLM pass. They appear on the /anti-patterns
+ *     page alongside detected rules with an 'llm' layer badge.
+ */
+
+// ─── Detection layers ────────────────────────────────────────────────
+
+/**
+ * Which layer catches each rule.
+ *
+ *  'cli':     static analysis or jsdom (works with `npx impeccable detect`
+ *             on files, no browser required)
+ *  'browser': requires real browser layout (getBoundingClientRect with
+ *             actual dimensions). Works via Puppeteer or the browser
+ *             extension, NOT via the CLI on raw HTML.
+ *  'llm':     no deterministic detector; only caught by /critique's LLM
+ *             assessment pass.
+ *
+ * Per tests/detect-antipatterns-browser.test.mjs: only two rules genuinely
+ * need real browser layout. Everything else is 'cli'.
+ */
+export const DETECTION_LAYERS = {
+  'side-tab': 'cli',
+  'border-accent-on-rounded': 'cli',
+  'overused-font': 'cli',
+  'single-font': 'cli',
+  'flat-type-hierarchy': 'cli',
+  'icon-tile-stack': 'cli',
+  'gradient-text': 'cli',
+  'ai-color-palette': 'cli',
+  'dark-glow': 'cli',
+  'nested-cards': 'cli',
+  'monotonous-spacing': 'cli',
+  'everything-centered': 'cli',
+  'bounce-easing': 'cli',
+  'all-caps-body': 'cli',
+  'pure-black-white': 'cli',
+  'gray-on-color': 'cli',
+  'low-contrast': 'cli',
+  'layout-transition': 'cli',
+  'tight-leading': 'cli',
+  'skipped-heading': 'cli',
+  'justified-text': 'cli',
+  'tiny-text': 'cli',
+  'wide-tracking': 'cli',
+  // Browser-only: need real layout measurements.
+  'cramped-padding': 'browser',
+  'line-length': 'browser',
+};
+
+export const LAYER_LABELS = {
+  cli: 'CLI',
+  browser: 'Browser',
+  llm: 'LLM only',
+};
+
+export const LAYER_DESCRIPTIONS = {
+  cli: 'Deterministic. Runs from `npx impeccable detect` on files, no browser required.',
+  browser: 'Deterministic, but needs real browser layout. Runs via the browser extension or Puppeteer, not the plain CLI.',
+  llm: 'Not caught by any deterministic detector. Flagged by /critique during its LLM design review.',
+};
+
+// ─── Visual examples ─────────────────────────────────────────────────
+
+/**
+ * One tiny inline HTML snippet per rule showing what the bad pattern
+ * looks like. Snippets use inline styles only and are sized to fit the
+ * rule card preview area (~100% wide, ~120px tall).
+ */
+export const VISUAL_EXAMPLES = {
+  'side-tab': `<div style="background: #fff; border: 1px solid #e8e4df; border-left: 4px solid oklch(60% 0.22 265); border-radius: 6px; padding: 14px 16px; width: 220px; font-family: system-ui, sans-serif; font-size: 13px; color: #111;"><div style="font-weight: 600; margin-bottom: 4px;">Alert title</div><div style="color: #666; font-size: 12px;">Thick colored stripe on one side.</div></div>`,
+
+  'border-accent-on-rounded': `<div style="background: #fff; border: 2px solid oklch(60% 0.22 290); border-radius: 16px; padding: 14px 18px; width: 220px; font-family: system-ui, sans-serif; font-size: 13px; color: #111;"><div style="font-weight: 600;">Rounded card</div><div style="color: #666; font-size: 12px;">Thick colored border clashes with the radius.</div></div>`,
+
+  'overused-font': `<div style="font-family: Inter, system-ui, sans-serif; font-size: 15px; color: #111; line-height: 1.4;"><div style="font-weight: 600; margin-bottom: 4px;">Just another Inter headline</div><div style="color: #555; font-size: 13px;">Every SaaS homepage looks like this.</div></div>`,
+
+  'single-font': `<div style="font-family: system-ui, sans-serif; font-size: 14px; color: #111;"><div style="font-size: 19px; font-weight: 600; margin-bottom: 6px;">Heading in the body font</div><div style="color: #555;">Body in the same font. No contrast. Flat.</div></div>`,
+
+  'flat-type-hierarchy': `<div style="font-family: system-ui, sans-serif; color: #111; line-height: 1.3;"><div style="font-size: 17px; font-weight: 600;">Heading</div><div style="font-size: 16px; font-weight: 500; margin: 2px 0;">Subheading</div><div style="font-size: 15px; color: #555;">Body text at almost the same size.</div></div>`,
+
+  'icon-tile-stack': `<div style="font-family: system-ui, sans-serif; color: #111;"><div style="width: 44px; height: 44px; border-radius: 10px; background: linear-gradient(135deg, oklch(62% 0.22 265), oklch(70% 0.20 320)); display: flex; align-items: center; justify-content: center; font-size: 20px; color: #fff; margin-bottom: 10px;">✦</div><div style="font-size: 14px; font-weight: 600; margin-bottom: 2px;">Feature name</div><div style="font-size: 12px; color: #666;">Rounded icon tile above heading.</div></div>`,
+
+  'gradient-text': `<div style="font-family: system-ui, sans-serif;"><div style="font-size: 28px; font-weight: 700; background: linear-gradient(135deg, oklch(65% 0.25 320), oklch(60% 0.25 265)); -webkit-background-clip: text; background-clip: text; color: transparent; line-height: 1.1;">Build the Future</div><div style="font-size: 12px; color: #888; margin-top: 4px;">Gradient text kills scannability.</div></div>`,
+
+  'ai-color-palette': `<div style="display: flex; gap: 6px;"><div style="width: 44px; height: 44px; border-radius: 6px; background: oklch(60% 0.22 265);"></div><div style="width: 44px; height: 44px; border-radius: 6px; background: oklch(62% 0.25 300);"></div><div style="width: 44px; height: 44px; border-radius: 6px; background: oklch(64% 0.25 340);"></div><div style="width: 44px; height: 44px; border-radius: 6px; background: oklch(70% 0.20 200);"></div></div>`,
+
+  'dark-glow': `<div style="background: #0a0b14; padding: 18px 20px; border-radius: 10px; font-family: system-ui, sans-serif;"><div style="color: oklch(78% 0.22 280); text-shadow: 0 0 12px oklch(78% 0.22 280 / 0.7); font-size: 16px; font-weight: 600;">Neon on dark</div><div style="color: oklch(60% 0.12 260); font-size: 12px; margin-top: 4px;">Cyberpunk-by-default slop.</div></div>`,
+
+  'nested-cards': `<div style="background: #f5f3ef; border: 1px solid #e0dcd4; border-radius: 10px; padding: 10px;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 8px; padding: 10px;"><div style="background: #f5f3ef; border: 1px solid #e8e4df; border-radius: 6px; padding: 8px; font-size: 12px; font-family: system-ui, sans-serif; color: #555;">Card inside card inside card.</div></div></div>`,
+
+  'monotonous-spacing': `<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div></div>`,
+
+  'everything-centered': `<div style="font-family: system-ui, sans-serif; text-align: center; color: #111;"><div style="font-size: 16px; font-weight: 600; margin-bottom: 6px;">Centered headline</div><div style="font-size: 12px; color: #555; margin-bottom: 10px;">Everything centered by default.</div><div style="display: inline-block; background: #111; color: #fff; padding: 6px 14px; border-radius: 6px; font-size: 12px;">Call to action</div></div>`,
+
+  'bounce-easing': `<div style="font-family: system-ui, sans-serif; color: #111; display: flex; align-items: center; gap: 10px;"><div style="width: 36px; height: 36px; border-radius: 50%; background: oklch(65% 0.22 265); animation: bouncey 0.9s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;"></div><div style="font-size: 12px; color: #555;">Bounce + elastic easing feels dated.</div><style>@keyframes bouncey { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }</style></div>`,
+
+  'all-caps-body': `<div style="font-family: system-ui, sans-serif; color: #111; font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; line-height: 1.5;">Long passages in uppercase are hard to read. We recognize words by their shape, which all-caps removes.</div>`,
+
+  'pure-black-white': `<div style="background: #ffffff; padding: 16px 18px; color: #000000; font-family: system-ui, sans-serif; font-size: 14px;"><div style="font-weight: 600; margin-bottom: 4px;">Pure black on pure white</div><div style="font-size: 12px; color: #000;">Neither exists in nature. Always tint.</div></div>`,
+
+  'gray-on-color': `<div style="background: oklch(60% 0.20 265); padding: 16px 18px; border-radius: 6px; font-family: system-ui, sans-serif;"><div style="color: #9ca3af; font-size: 13px;">Gray text on a colored background. Washed out and hard to read.</div></div>`,
+
+  'low-contrast': `<div style="background: #fff; padding: 16px 18px; font-family: system-ui, sans-serif;"><div style="color: #d4d4d4; font-size: 13px;">Light gray text on a white background. 1.6:1 contrast, fails WCAG.</div></div>`,
+
+  'layout-transition': `<div style="font-family: system-ui, sans-serif; color: #111; display: flex; align-items: center; gap: 10px;"><div style="background: oklch(65% 0.22 265); border-radius: 6px; animation: janky 1.2s ease-in-out infinite; width: 60px; height: 30px;"></div><div style="font-size: 12px; color: #555;">Animating width/height causes layout jank.</div><style>@keyframes janky { 0%,100% { width: 60px; } 50% { width: 120px; } }</style></div>`,
+
+  'cramped-padding': `<div style="font-family: system-ui, sans-serif;"><button style="background: #111; color: #fff; border: none; border-radius: 4px; padding: 2px 6px; font-size: 13px; font-weight: 500;">Buy now</button> <span style="color: #555; font-size: 12px; margin-left: 8px;">2px vertical padding.</span></div>`,
+
+  'tight-leading': `<div style="font-family: system-ui, sans-serif; font-size: 13px; color: #111; line-height: 1.0; max-width: 220px;">Tight leading makes multi-line body text feel crammed and hard for the eye to track between lines.</div>`,
+
+  'skipped-heading': `<div style="font-family: system-ui, sans-serif; color: #111;"><h1 style="font-size: 20px; font-weight: 700; margin: 0 0 4px;">Page title (h1)</h1><h3 style="font-size: 13px; font-weight: 600; margin: 0; color: #555;">Subsection (h3), skipped h2</h3></div>`,
+
+  'justified-text': `<div style="font-family: system-ui, sans-serif; font-size: 12px; color: #111; text-align: justify; max-width: 230px; line-height: 1.5;">Justified text on screens creates rivers of whitespace because browsers can't hyphenate well. Leave this for print.</div>`,
+
+  'tiny-text': `<div style="font-family: system-ui, sans-serif; color: #111;"><div style="font-size: 15px; margin-bottom: 6px;">Regular body text</div><div style="font-size: 9px; color: #555;">And then fine print at 9 pixels that no one will ever read.</div></div>`,
+
+  'wide-tracking': `<div style="font-family: system-ui, sans-serif; font-size: 13px; color: #111; letter-spacing: 0.22em; max-width: 230px; line-height: 1.6;">Wide tracking on body text slows reading by breaking up natural character groupings.</div>`,
+
+  'line-length': `<div style="font-family: system-ui, sans-serif; font-size: 13px; color: #111; line-height: 1.55; max-width: 100%;">Paragraphs wider than roughly 75 characters per line become fatiguing because the eye has to track an excessive distance back to the start of the next line, losing its place.</div>`,
+
+  // ── LLM-only rule visuals ─────────────────────────────────────────
+
+  'monospace-as-technical': `<div style="font-family: 'Courier New', monospace; color: #111;"><div style="font-size: 18px; font-weight: 700; margin-bottom: 6px;">TECHNICAL_TOOL</div><div style="font-size: 11px; color: #555;">Mono for "developer" vibes. Lazy.</div></div>`,
+
+  'dark-mode-default': `<div style="background: #0f1117; padding: 18px; border-radius: 8px; font-family: system-ui, sans-serif;"><div style="color: #e5e7eb; font-size: 14px; font-weight: 600; margin-bottom: 4px;">Dark by default</div><div style="color: #9ca3af; font-size: 11px;">Defaulting to dark is a retreat from a decision.</div></div>`,
+
+  'everything-in-cards': `<div style="background: #fff; border: 1px solid #e8e4df; border-radius: 8px; padding: 10px;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; padding: 8px; font-family: system-ui, sans-serif; font-size: 12px; color: #111;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 4px; padding: 6px;">Title</div></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; padding: 8px; margin-top: 6px; font-family: system-ui, sans-serif; font-size: 11px; color: #555;">Card around every single thing.</div></div>`,
+
+  'identical-card-grids': `<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; font-family: system-ui, sans-serif;">${'<div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; padding: 10px; display: flex; flex-direction: column; align-items: flex-start; gap: 4px;"><div style="width: 18px; height: 18px; background: oklch(62% 0.20 265); border-radius: 4px;"></div><div style="font-size: 10px; font-weight: 600; color: #111;">Feature</div><div style="font-size: 9px; color: #888;">Short copy.</div></div>'.repeat(6)}</div>`,
+
+  'hero-metric-layout': `<div style="font-family: system-ui, sans-serif; text-align: left;"><div style="font-size: 42px; font-weight: 800; background: linear-gradient(135deg, oklch(65% 0.25 265), oklch(65% 0.25 340)); -webkit-background-clip: text; background-clip: text; color: transparent; line-height: 1;">10M+</div><div style="font-size: 10px; color: #888; text-transform: uppercase; letter-spacing: 0.1em; margin-top: 2px;">Active users</div><div style="display: flex; gap: 14px; margin-top: 10px; font-size: 10px; color: #555;"><span><strong>99.9%</strong> uptime</span><span><strong>200ms</strong> p50</span></div></div>`,
+
+  'glassmorphism': `<div style="position: relative; width: 100%; height: 100%; background: linear-gradient(135deg, oklch(70% 0.22 265), oklch(70% 0.25 340)); border-radius: 10px; overflow: hidden; display: flex; align-items: center; justify-content: center;"><div style="background: rgba(255,255,255,0.25); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); border: 1px solid rgba(255,255,255,0.4); border-radius: 10px; padding: 14px 18px; color: #fff; font-family: system-ui, sans-serif; font-size: 12px; font-weight: 600; box-shadow: 0 8px 30px rgba(0,0,0,0.12);">Frosted glass card</div></div>`,
+
+  'sparkline-decoration': `<div style="background: #fff; border: 1px solid #e8e4df; border-radius: 8px; padding: 14px 16px; width: 220px; font-family: system-ui, sans-serif;"><div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px;"><div><div style="font-size: 10px; color: #888; text-transform: uppercase; letter-spacing: 0.08em;">Revenue</div><div style="font-size: 20px; font-weight: 700; color: #111;">$42.1k</div></div><svg width="60" height="28" viewBox="0 0 60 28" style="flex-shrink: 0;"><polyline points="0,20 10,18 20,22 30,10 40,14 50,6 60,12" stroke="oklch(62% 0.22 265)" stroke-width="2" fill="none"/></svg></div><div style="font-size: 10px; color: #888;">Tiny chart, no real information.</div></div>`,
+
+  'generic-drop-shadows': `<div style="display: flex; gap: 10px;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 10px; width: 70px; height: 70px; box-shadow: 0 10px 30px rgba(0,0,0,0.08);"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 10px; width: 70px; height: 70px; box-shadow: 0 10px 30px rgba(0,0,0,0.08);"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 10px; width: 70px; height: 70px; box-shadow: 0 10px 30px rgba(0,0,0,0.08);"></div></div>`,
+
+  'modal-reflex': `<div style="position: relative; width: 100%; height: 100%; background: #f5f3ef; border-radius: 8px; overflow: hidden;"><div style="position: absolute; inset: 0; background: rgba(0,0,0,0.35);"></div><div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #fff; border-radius: 8px; padding: 14px 18px; width: 200px; font-family: system-ui, sans-serif; box-shadow: 0 20px 60px rgba(0,0,0,0.2);"><div style="font-size: 13px; font-weight: 600; color: #111; margin-bottom: 4px;">Are you sure?</div><div style="font-size: 11px; color: #666; margin-bottom: 8px;">Really, truly sure about this?</div><div style="display: flex; gap: 6px; justify-content: flex-end;"><div style="background: #eee; color: #555; padding: 4px 8px; border-radius: 4px; font-size: 10px;">Cancel</div><div style="background: oklch(60% 0.22 265); color: #fff; padding: 4px 8px; border-radius: 4px; font-size: 10px;">OK</div></div></div></div>`,
+
+  'every-button-primary': `<div style="display: flex; flex-direction: column; gap: 6px; font-family: system-ui, sans-serif;"><div style="display: flex; gap: 6px;"><button style="background: oklch(60% 0.22 265); color: #fff; border: none; border-radius: 5px; padding: 6px 12px; font-size: 11px; font-weight: 600;">Save</button><button style="background: oklch(60% 0.22 265); color: #fff; border: none; border-radius: 5px; padding: 6px 12px; font-size: 11px; font-weight: 600;">Cancel</button><button style="background: oklch(60% 0.22 265); color: #fff; border: none; border-radius: 5px; padding: 6px 12px; font-size: 11px; font-weight: 600;">Delete</button></div><div style="font-size: 10px; color: #888;">Every action shouts equally.</div></div>`,
+
+  'redundant-headers': `<div style="font-family: system-ui, sans-serif; color: #111; max-width: 230px;"><div style="font-size: 14px; font-weight: 600; margin-bottom: 4px;">Overview</div><div style="font-size: 11px; color: #555; line-height: 1.5;">This is the overview section, which provides an overview of the overview.</div></div>`,
+
+  'mobile-amputation': `<div style="font-family: system-ui, sans-serif;"><div style="display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: #fff; border: 1px solid #e8e4df; border-radius: 6px; margin-bottom: 4px; font-size: 12px; color: #999; text-decoration: line-through;"><span>Export to CSV</span></div><div style="font-size: 10px; color: #888; margin-top: 4px;">"Not available on mobile."</div></div>`,
+};
+
+/**
+ * Anti-patterns that live in the /impeccable skill's DON'T list but
+ * don't have a deterministic detector. These can only be caught by
+ * /critique running an LLM assessment pass.
+ *
+ * Each entry looks like a detection rule: id, category, name,
+ * description, skillSection. The generator merges these into the
+ * grouped sections alongside detected rules with an 'llm' layer badge.
+ */
+// ─── Gallery: real examples in the wild ──────────────────────────────
+
+/**
+ * Curated real-world examples of anti-patterns caught in the wild.
+ * Each entry maps to:
+ *   - public/antipattern-images/{id}.png  (preview thumbnail)
+ *   - public/antipattern-examples/{id}.html  (standalone live example)
+ * Rendered as a dedicated section on the /anti-patterns page, replacing
+ * the old /gallery route which was confusingly labeled in the top nav.
+ */
+export const GALLERY_ITEMS = [
+  {
+    id: 'purple-gradients',
+    title: 'Purple Gradients Everywhere',
+    desc:
+      'The AI color palette: purple-to-blue gradients on everything. Buttons, text, backgrounds, orbs. The new "make it pop."',
+  },
+  {
+    id: 'lazy-cool',
+    title: 'Lazy "Cool"',
+    desc:
+      'Glassmorphism, neon glows, blurred orbs, monospace everything. Looks like a hackathon project, not a product.',
+  },
+  {
+    id: 'lazy-impact',
+    title: 'Lazy "Impact"',
+    desc:
+      'When in doubt, animate everything. Bouncing buttons, wiggling icons, gradient text, floating badges. Motion without meaning.',
+  },
+  {
+    id: 'thick-border-cards',
+    title: 'Side-Tab Cards',
+    desc:
+      'A thick colored border on one side of a rounded card. The single most recognizable tell of AI-generated UI.',
+  },
+  {
+    id: 'cardocalypse',
+    title: 'Cardocalypse',
+    desc:
+      'Cards inside cards inside cards. Five levels of nesting, each with its own padding and shadow.',
+  },
+  {
+    id: 'layout-templates',
+    title: 'Copy-Paste Layouts',
+    desc:
+      'The same hero-metric-features template repeated with different colors. When every section looks the same, nothing stands out.',
+  },
+  {
+    id: 'inter-everywhere',
+    title: 'Inter Everywhere',
+    desc:
+      'One font for everything. Headings, body, labels, buttons. No typographic hierarchy, no personality, no design.',
+  },
+  {
+    id: 'massive-icons',
+    title: 'Massive Icons',
+    desc:
+      'Icon containers larger than the content they introduce. When the decoration is bigger than the message, priorities are backwards.',
+  },
+  {
+    id: 'bad-contrast',
+    title: 'Bad Contrast Choices',
+    desc:
+      'Gray text on colored backgrounds, low-contrast labels, unreadable combinations. Looking good and being readable should not conflict.',
+  },
+  {
+    id: 'redundant-ux-writing',
+    title: 'Redundant UX Writing',
+    desc:
+      'Label, sublabel, helper text, and hint text all saying the same thing in slightly different words. Say it once, say it well.',
+  },
+  {
+    id: 'modal-abuse',
+    title: 'Modal Abuse',
+    desc:
+      'Complex settings crammed into a modal. If it needs a scroll bar and three columns, it deserves its own page.',
+  },
+];
+
+// ─── LLM-only rules ──────────────────────────────────────────────────
+
+export const LLM_ONLY_RULES = [
+  {
+    id: 'monospace-as-technical',
+    category: 'slop',
+    name: 'Monospace as "technical" shorthand',
+    description:
+      'Using a monospace typeface to signal "developer / technical" vibes. Reach for real type choices instead of a lazy stereotype.',
+    skillSection: 'Typography',
+  },
+  {
+    id: 'dark-mode-default',
+    category: 'slop',
+    name: 'Defaulting to dark mode for "safety"',
+    description:
+      'Defaulting to light mode to be safe is the inverse of defaulting to dark mode to look cool. Either way you are retreating from a decision.',
+    skillSection: 'Color & Contrast',
+  },
+  {
+    id: 'everything-in-cards',
+    category: 'slop',
+    name: 'Wrapping everything in cards',
+    description:
+      'Not every piece of content needs a bordered container. Spacing and alignment create visual grouping without the overhead of a card.',
+    skillSection: 'Layout & Space',
+  },
+  {
+    id: 'identical-card-grids',
+    category: 'slop',
+    name: 'Identical card grids',
+    description:
+      'Same-sized cards with icon + heading + text repeated endlessly. The default AI homepage layout.',
+    skillSection: 'Layout & Space',
+  },
+  {
+    id: 'hero-metric-layout',
+    category: 'slop',
+    name: 'Hero metric layout',
+    description:
+      'Big number, small label, three supporting stats, gradient accent. Used everywhere, trusted nowhere.',
+    skillSection: 'Layout & Space',
+  },
+  {
+    id: 'glassmorphism',
+    category: 'slop',
+    name: 'Glassmorphism everywhere',
+    description:
+      'Blur effects, glass cards, and glow borders used as decoration rather than to solve a real layering problem.',
+    skillSection: 'Visual Details',
+  },
+  {
+    id: 'sparkline-decoration',
+    category: 'slop',
+    name: 'Sparklines as decoration',
+    description:
+      'Tiny charts that look sophisticated but convey no meaningful information. If the data matters, give it room.',
+    skillSection: 'Visual Details',
+  },
+  {
+    id: 'generic-drop-shadows',
+    category: 'slop',
+    name: 'Rounded rectangles with generic drop shadows',
+    description:
+      'The safest, most forgettable shape on the web. Could be the output of any AI. Commit to a stronger visual treatment.',
+    skillSection: 'Visual Details',
+  },
+  {
+    id: 'modal-reflex',
+    category: 'slop',
+    name: 'Reaching for modals by reflex',
+    description:
+      'Modals interrupt the user and are lazy as a design default. Use them only when there is truly no better place for the interaction.',
+    skillSection: 'Visual Details',
+  },
+  {
+    id: 'every-button-primary',
+    category: 'quality',
+    name: 'Every button is a primary button',
+    description:
+      'When every button looks equally important, nothing reads as the primary action. Use ghost buttons, text links, and secondary styles to build hierarchy.',
+    skillSection: 'Interaction',
+  },
+  {
+    id: 'redundant-headers',
+    category: 'quality',
+    name: 'Redundant information',
+    description:
+      'Intros that restate the heading. Section labels that repeat the page title. Cards that echo their own caption. Make every word earn its place.',
+    skillSection: 'Interaction',
+  },
+  {
+    id: 'mobile-amputation',
+    category: 'quality',
+    name: 'Amputating features on mobile',
+    description:
+      'Hiding critical functionality on mobile because it is inconvenient. Adapt the interface to the context, do not strip it.',
+    skillSection: 'Responsive',
+  },
+];

content/site/partials/header.html 🔗

@@ -0,0 +1,20 @@
+<!-- site-header v1 -->
+<header class="site-header" data-site-header>
+  <a href="/" class="site-header-brand" aria-label="Impeccable home">
+    <svg class="site-header-brand-logo" viewBox="0 0 32 32" aria-hidden="true"><rect width="32" height="32" rx="6" fill="#1a1a1a"/><text x="16" y="24" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="500" fill="#f5f3ef" text-anchor="middle">/</text></svg>
+    <span class="site-header-brand-name">Impeccable</span>
+  </a>
+  <div class="site-header-right">
+    <nav class="site-header-nav" aria-label="Primary">
+      <a href="/" data-nav="home">Home</a>
+      <a href="/skills" data-nav="docs">Docs</a>
+      <a href="/anti-patterns" data-nav="anti-patterns">Anti-Patterns</a>
+      <a href="/visual-mode" data-nav="visual-mode">Visual Mode</a>
+    </nav>
+    <a href="https://github.com/pbakaus/impeccable" class="site-header-github" target="_blank" rel="noopener" aria-label="Impeccable on GitHub, 18k stars">
+      <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
+      <span class="site-header-github-label">18k</span>
+      <svg class="site-header-github-star" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2l2.76 6.36L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.24-.91L12 2z"/></svg>
+    </a>
+  </div>
+</header>

content/site/skills/adapt.md 🔗

@@ -0,0 +1,40 @@
+---
+tagline: "Make designs work across screens, devices, and contexts without amputating features."
+---
+
+## When to use it
+
+`/adapt` is for taking a design built for one context and making it work in another. Mobile from desktop, tablet from mobile, print from web, embedded from standalone, email from dashboard. Reach for it when the source design is solid but falls apart at other breakpoints, on touch, or in a different container.
+
+Not for building responsive from scratch. For that, start with `/impeccable` and shape the layout responsive-first. Adapt is for the "we never thought about mobile" backfill.
+
+## How it works
+
+The skill works through four dimensions of contextual fit:
+
+1. **Breakpoints and fluid layout**: collapse multi-column to single, adjust clamp ranges, introduce new breakpoints where the design genuinely breaks.
+2. **Touch targets**: minimum 44px hit areas, sufficient spacing between adjacent targets, larger tap zones than visual bounds where needed.
+3. **Navigation patterns**: desktop sidebars become mobile bottom nav or slide-outs, dense toolbars collapse into menus, hover states get touch equivalents.
+4. **Content priority**: decide what must be visible, what can collapse into disclosures, what can be removed entirely for that context.
+
+The non-negotiable rule: adapt, do not amputate. Critical functionality cannot disappear on mobile just because it is inconvenient. Find a way to fit it, redesign the interaction, or reconsider whether it was really critical on desktop.
+
+## Try it
+
+```
+/adapt the settings page for mobile
+```
+
+Expected changes:
+
+- Three-column grid becomes single column with section headers acting as sticky dividers
+- Sidebar nav moves to a horizontal scroller above the content
+- Toggles gain 8px vertical padding so they meet 44px touch targets
+- Inline help text moves to tap-to-reveal, not hover
+- The "Danger zone" section expands fully on mobile instead of collapsing, because it contains irreversible actions and we want users to see them clearly
+
+## Pitfalls
+
+- **Amputating features.** If the mobile version hides things the desktop version can do, that is a regression, not an adaptation. Fight for the feature.
+- **Treating mobile as "smaller desktop".** Mobile is a different context: thumbs, interruption, short sessions. Adapt to the context, not to the viewport width.
+- **Skipping `/harden` afterward.** Responsive layouts reveal edge cases. Run hardening after adapt to catch the ones that only show up at 320px.

content/site/skills/animate.md 🔗

@@ -0,0 +1,42 @@
+---
+tagline: "Purposeful motion that conveys state, not decoration."
+---
+
+## When to use it
+
+`/animate` is for interfaces that feel lifeless, where state changes are instant and jarring, where loading just pops in, where the user never quite trusts that their click registered. Use it to add the small motions that communicate what is happening: entrances, exits, feedback, transitions between states.
+
+Do not use it to add bounces or elastic springs for the sake of energy. That is decoration, and this skill will not give it to you.
+
+## How it works
+
+The skill identifies static moments that would benefit from motion, then applies them with strict discipline:
+
+1. **Entrances and exits**: elements appear and leave with 200 to 300ms fades plus subtle Y or scale, never layout properties.
+2. **State feedback**: hover, active, focus, loading, success all communicate via motion instead of sudden swaps.
+3. **Transitions between views**: shared-element transitions where it makes sense, fade-through otherwise.
+4. **Progress and loading**: skeleton screens, determinate bars, motion that says "still working".
+5. **Reduced motion**: every animation has a `prefers-reduced-motion` fallback.
+
+Easing is always exponential (ease-out-quart, quint, or expo) because real objects decelerate smoothly. No bounce, no elastic, no linear for anything except progress indicators.
+
+The skill animates `transform` and `opacity` only. If you find yourself animating `width`, `height`, `top`, or `left`, it is doing the wrong thing. Use `grid-template-rows` for height transitions.
+
+## Try it
+
+```
+/animate the sign-up flow
+```
+
+Typical additions:
+
+- Email input gets a focus glow on focus-visible (opacity + shadow, 180ms)
+- Submit button shows a spinner inside itself on loading state, not a separate spinner next to it
+- Success screen enters with opacity + translateY(8px), 260ms, ease-out-quart
+- Error message slides down with grid-template-rows (not height), 220ms
+- `@media (prefers-reduced-motion: reduce)` fallback for every transition
+
+## Pitfalls
+
+- **Asking for "more animation".** Animate is not a dial. It adds where motion communicates, not everywhere.
+- **Removing the reduced-motion fallbacks.** The skill adds them automatically. Non-negotiable for accessibility.

content/site/skills/arrange.md 🔗

@@ -0,0 +1,41 @@
+---
+tagline: "Fix layout, spacing, and visual rhythm."
+---
+
+## When to use it
+
+`/arrange` is for pages where nothing is technically wrong but nothing is breathing either. Equal padding everywhere, monotonous card grids, content that runs edge to edge, hierarchy that relies on size alone. Reach for it when a layout "feels off" and you cannot articulate why.
+
+Good triggers: "everything feels crowded", "it reads like a wall", "I do not know where to look first".
+
+## How it works
+
+The skill runs through five layout dimensions:
+
+1. **Spacing**: is the spacing scale consistent or are there random 13px gaps, are related elements grouped tightly with generous space between groups, is there any rhythm at all.
+2. **Visual hierarchy**: does the eye land on the primary action within 2 seconds, is the hierarchy doing real work or is everything shouting.
+3. **Grid and structure**: is there an underlying grid or is the layout random, are elements aligned to baselines.
+4. **Rhythm**: does the page alternate between tight and generous spacing, or is everything uniform.
+5. **Density**: is the layout cramped or is it wasteful, does density match the content type.
+
+Fixes usually involve rebuilding the spacing scale, introducing asymmetry, collapsing monotonous grids into a mixed layout with hero and supporting elements, and giving the primary action real space.
+
+## Try it
+
+```
+/arrange the settings page
+```
+
+Typical changes:
+
+- Spacing scale unified to 8 / 16 / 24 / 48 / 96px
+- Section breaks at 48px, row gaps at 16px, form field groups at 8px
+- Primary actions pulled out of the form flow with 32px buffer
+- Decorative borders removed, replaced with spacing-driven grouping
+- Sidebar and main column proportions rebalanced (280 / flex vs 25 / 75)
+
+## Pitfalls
+
+- **Confusing arrange with distill.** If the problem is too many things, run `/distill` first. Arrange is for arranging what is already the right set.
+- **Expecting it to rescue a broken grid.** If the page has no grid at all, arrange will build one. Just know that the diff is going to be larger than you expect.
+- **Ignoring the hierarchy verdict.** If arrange says "nothing is primary", no amount of spacing work fixes that. You need a content decision, not a layout tweak.

content/site/skills/audit.md 🔗

@@ -0,0 +1,50 @@
+---
+tagline: "Five-dimension technical quality check with P0 to P3 severity."
+---
+
+## When to use it
+
+`/audit` is the technical counterpart to `/critique`. Where `/critique` asks "does this feel right", `/audit` asks "does this hold up". It runs accessibility, performance, theming, responsive design, and anti-pattern checks against the implementation, scores each dimension 0 to 4, and produces a plan with P0 to P3 severity ratings.
+
+Use it before shipping, during a quality sprint, or whenever a tech lead says "we should really look at accessibility".
+
+## How it works
+
+The skill scans your code across five dimensions:
+
+1. **Accessibility**: WCAG contrast, ARIA, keyboard nav, semantic HTML, form labels.
+2. **Performance**: layout thrashing, expensive animations, missing lazy loading, bundle weight.
+3. **Theming**: hard-coded colors, dark mode coverage, token consistency.
+4. **Responsive**: breakpoint behavior, touch targets, mobile viewport handling.
+5. **Anti-patterns**: the same deterministic 25 checks the detector runs.
+
+Each dimension gets a 0 to 4 score. Each finding gets a severity: P0 blocks the release, P1 should fix this sprint, P2 is next cycle, P3 is polish. You get back a single document you can paste into a ticket tracker.
+
+Audit does not fix anything. It documents. Route the findings to `/polish`, `/harden`, or `/optimize` depending on the category.
+
+## Try it
+
+```
+/audit the checkout flow
+```
+
+Expected output:
+
+```
+Accessibility: 2/4 (partial)
+  P0: Missing form labels on 4 inputs
+  P1: Contrast 3.1:1 on disabled button state
+  P2: No visible focus indicator on custom dropdown
+
+Performance: 3/4 (good)
+  P1: Hero image not lazy-loaded (340KB)
+  ...
+```
+
+Hand the P0s to `/harden`, the theming and typography P1s to `/typeset` and `/polish`, the rest to `/polish`.
+
+## Pitfalls
+
+- **Confusing it with `/critique`.** Audit is implementation quality. Critique is design quality. Run both for a full picture.
+- **Fixing P3s before P0s.** The severity scale exists for a reason. Start at the top.
+- **Skipping the dimensions you think are fine.** Theming and responsive are the ones most people assume are fine until they are not.

content/site/skills/bolder.md 🔗

@@ -0,0 +1,40 @@
+---
+tagline: "Push safe designs toward impact without sliding into chaos."
+---
+
+## When to use it
+
+Reach for `/bolder` when the interface looks like every other interface. Generic sans, medium weights, soft shadows, modest accent color, reasonable spacing, forgettable. The design is not wrong, it is just safe. Use bolder when a project can handle presence and the current state is not bringing any.
+
+Do not use it on dashboards people stare at for hours. Boldness earns its place on marketing pages, hero moments, and content features. Not in operator tools.
+
+## How it works
+
+The skill amplifies four axes without breaking usability:
+
+1. **Scale**: display type gets pushed to clamp(3rem, 6vw, 6rem) or beyond. Headlines that fill the viewport, not hedge it.
+2. **Weight contrast**: light 300 against heavy 800 instead of medium against regular. Real tension, not a shrug.
+3. **Color commitment**: the accent color shows up at full strength, not diluted. Backgrounds can take a stance (ink, accent, cream) instead of all-paper.
+4. **Compositional confidence**: asymmetry, off-grid, pullquotes, hanging punctuation, scale jumps. The layout has a voice.
+
+The skill does not add more. It amplifies what is already there. If the design has three colors, bolder does not add a fourth, it commits harder to the three.
+
+## Try it
+
+```
+/bolder the landing page hero
+```
+
+Expected changes:
+
+- Hero heading from 3rem to clamp(3.5rem, 7vw, 6.5rem), display font, weight 700
+- Subhead from regular to italic at 1.5rem, pulled 8px left of the heading for optical alignment
+- Background switches from paper to a cream-to-paper gradient, creating a warmer container
+- CTA button fills, drops shadow removed, border radius reduced, hover state inverts colors
+- Supporting image pushed slightly off-grid with a negative top margin, creating asymmetry
+
+## Pitfalls
+
+- **Running it on the wrong page.** Product dashboards, settings, and forms should not be bold. They should be legible. Use `/layout` or `/polish` instead.
+- **Confusing bold with loud.** Bold means committed and confident. Loud means shouting. Bolder is the former. If the result feels aggressive, follow up with `/quieter`.
+- **Pairing it with `/delight` in the same pass.** Delight works best against a stable visual baseline. Bold first, stabilize, then delight.

content/site/skills/clarify.md 🔗

@@ -0,0 +1,42 @@
+---
+tagline: "Rewrite confusing UX copy so interfaces explain themselves."
+---
+
+## When to use it
+
+`/clarify` is for interface text that makes people stop and think. Confusing labels, ambiguous button copy, error messages that blame the user, tooltips that repeat the label, empty states that say nothing useful. Use it when the problem is not the layout or the color, it is the words.
+
+Good triggers: "users do not understand this field", "the error message is not helpful", "I cannot write good button copy", "this tooltip is a waste".
+
+## How it works
+
+The skill rewrites text across the surfaces where most UX copy problems live:
+
+1. **Labels and field hints**: direct, specific, say what is expected.
+2. **Button copy**: verb-first, describes the outcome, not the action. "Save changes" not "OK".
+3. **Error messages**: explain what went wrong, whose fault it is, and what to do next. Never blame the user.
+4. **Empty states**: orient the user, explain why the state is empty, offer a next step.
+5. **Tooltips and helper text**: add information the label cannot carry, never restate it.
+6. **Confirmation dialogs**: name the consequences, not the action.
+
+The skill uses the audience and mental state from `.impeccable.md` to tune voice. Technical audience gets precise language. Consumer audience gets plain speech. Rushed users get short text. Anxious users (payment, delete) get reassurance.
+
+## Try it
+
+```
+/clarify the billing form
+```
+
+Before and after, typical:
+
+- Label "Billing address" → "Address on your card"
+- Placeholder "Enter your VAT ID" → "VAT ID (optional, for business)"
+- Error "Invalid input" → "This card number is 15 digits. You entered 14."
+- Button "Submit" → "Charge $29 and subscribe"
+- Empty state "No transactions yet" → "Your first charge will show up here after your first order."
+
+## Pitfalls
+
+- **Writing cleverer, not clearer.** Clarify is not for voice upgrades. If the copy is already clear, do not reach for this skill. Use `/delight` instead when you want personality.
+- **Skipping the audience question.** Clarify needs to know who is reading. If `.impeccable.md` does not specify audience technical level, the rewrites will be generic.
+- **Running clarify on marketing copy.** Clarify is for functional UX text: labels, errors, instructions. Marketing copy needs a different set of moves and a human writer.

content/site/skills/colorize.md 🔗

@@ -0,0 +1,38 @@
+---
+tagline: "Add strategic color to monochrome interfaces without going garish."
+---
+
+## When to use it
+
+`/colorize` is the counterweight to "everything is gray". Dashboards that read as a beige wall, forms with no accent, content pages that could be any SaaS product. Reach for it when the interface is functional but emotionally flat, and you want warmth without tipping into the AI color palette (purple-to-pink, cyan neon, dark mode glow).
+
+## How it works
+
+The skill starts by reading your brand color if one exists, then decides where color earns its place:
+
+1. **Primary action** gets the strongest expression of the brand hue.
+2. **Secondary accents** get muted or tinted variants, not a second full color.
+3. **Neutrals** get tinted toward the brand hue at low chroma (around 0.005 to 0.01), which is nearly invisible per pixel but creates subconscious cohesion.
+4. **Content categories** get a limited, intentional accent system, not a rainbow.
+
+Importantly, it uses OKLCH rather than HSL so that equal lightness steps look equal. As lightness moves toward the extremes, chroma drops automatically. This is how you get color that feels considered instead of computed.
+
+## Try it
+
+```
+/colorize the dashboard
+```
+
+Expected diff:
+
+- Brand color moved from a hardcoded hex to `--color-accent: oklch(62% 0.18 240)`
+- Neutrals tinted with 0.007 chroma toward the brand hue
+- Primary button gets the full accent, secondary buttons get ink/mist
+- Chart series uses 3 distinct hues, all at matched lightness so no series visually dominates
+- Empty state illustration picks up a soft accent wash
+
+## Pitfalls
+
+- **Running it without a brand hue.** Colorize needs a starting point. If `.impeccable.md` does not specify one, it will ask. Do not let it pick from the AI color palette defaults.
+- **Expecting it to fix the AI color palette problem.** If your design already has purple gradients and cyan neon, you need `/quieter` first, then colorize can rebuild.
+- **Using it on already-colorful interfaces.** That is a `/quieter` job. Colorize adds, it does not subtract.

content/site/skills/critique.md 🔗

@@ -0,0 +1,43 @@
+---
+tagline: "A design review with scoring, persona tests, and automated detection."
+---
+
+## When to use it
+
+Reach for `/critique` when you want an honest second opinion on something you already built. Not "does it work" but "is it any good". The skill scores your interface against Nielsen's 10 heuristics, runs cognitive load checks, tests through persona lenses, and cross-references an automated detector for 25 concrete anti-patterns.
+
+Use it when a page is functionally done and you want to know if it reads as intentional or as AI slop.
+
+## How it works
+
+`/critique` runs two independent assessments in parallel so they do not bias each other.
+
+The first is an **LLM design review**: the model reads your source, visually inspects the live page if browser automation is available, and walks the impeccable skill's full DO/DON'T catalog. It scores Nielsen's heuristics, counts cognitive load failures, traces the emotional journey through the flow, and flags AI slop.
+
+The second is an **automated detector** (`npx impeccable detect`) that deterministically finds gradient text, purple palettes, side-tab borders, nested cards, line length problems, and the other visible fingerprints of generic AI output.
+
+The two reports merge into one prioritized list: what is working, the three to five things that need fixing, and the provocative questions worth answering before shipping.
+
+## Try it
+
+Point it at a page:
+
+```
+/critique the homepage hero
+```
+
+You get back a scored report. Typical shape:
+
+- **AI slop verdict**: pass / fail with the specific tells
+- **Heuristic scores**: 10 numbers, 0 to 4
+- **Cognitive load**: failure count out of 8
+- **Priority issues**: three to five items, each with what, why, and fix
+- **Questions to answer**: the ones the interface itself cannot decide for you
+
+From there, pair with `/polish` or `/distill` to act on the fixes.
+
+## Pitfalls
+
+- **Running it on incomplete work.** Critique is for finished pages. An empty state with three TODOs will score badly because it is not done, not because it is bad.
+- **Ignoring the questions at the end.** They are usually the highest-leverage fixes.
+- **Treating the heuristic scores as a grade.** They are diagnostic, not evaluative. A 3/4 on a heuristic that matters less for your context is fine.

content/site/skills/delight.md 🔗

@@ -0,0 +1,42 @@
+---
+tagline: "Small moments of personality that turn functional into memorable."
+---
+
+## When to use it
+
+`/delight` is for interfaces that work but do not feel like anything. Use it when the core experience is solid and you want to add the small human touches that make people remember it: a considered empty state, a loading message with a point of view, a success animation that feels earned, a microcopy moment that makes someone smile.
+
+It is a finishing skill. Never the first thing you run on a new build.
+
+## How it works
+
+The skill hunts for delight opportunities in the places most designers skip:
+
+1. **Empty states**: instead of "No items yet", something with personality appropriate to the brand.
+2. **Loading and waiting moments**: the best products turn waits into content.
+3. **Success feedback**: a moment of celebration when something worth celebrating happens.
+4. **Microcopy**: button labels, tooltips, error messages, placeholder text. Tiny copy with taste.
+5. **Easter eggs and secondary states**: things users discover that reward paying attention.
+
+The skill reads the brand tone from `.impeccable.md`. A serious analytics tool gets serious delight (dry, precise, a little clever). A playful consumer app gets more overt personality. It does not force humor where humor is wrong for the audience.
+
+The rule is: every delight moment must still work perfectly if you delete the delight. Nothing depends on the smile.
+
+## Try it
+
+```
+/delight the first-run experience
+```
+
+Expected additions:
+
+- Empty dashboard replaces "No data yet" with "Your dashboard is quiet. Let's fix that." plus a single-action CTA.
+- Initial sync gets a 3-state loading message that advances: "Finding your accounts... / Pulling the last 30 days... / Making it look good...".
+- First successful action triggers a one-time toast with a tiny celebratory moment. After that, just a quiet checkmark.
+- Help tooltip on the tricky field has a voice that sounds like a person wrote it.
+
+## Pitfalls
+
+- **Forcing humor.** Not every brand is playful. If the brand voice in `.impeccable.md` is "clinical and precise", delight adds clever restraint, not jokes.
+- **Over-decorating.** One moment of delight is memorable. Twenty becomes noise. The skill is conservative on purpose.
+- **Running delight before polish.** Polish fixes what is wrong. Delight adds what is missing. In that order.

content/site/skills/distill.md 🔗

@@ -0,0 +1,44 @@
+---
+tagline: "Ruthless subtraction. Strip designs to their essence."
+---
+
+## When to use it
+
+`/distill` removes what should not be there. Competing buttons, redundant information, decorative borders, three fonts where one works, six navigation items where three belong. Use it when an interface feels cluttered, busy, or like it is trying to do too much at once.
+
+Reach for it after `/critique` flags "cognitive load" or "visual noise", or any time a page has grown by accretion and no one has done the editing.
+
+## How it works
+
+The skill starts from one question: what is the single job this interface is trying to do? Everything that does not help that job is on the chopping block.
+
+It works in two passes:
+
+1. **Assess the complexity sources**. Too many elements, excessive variation, information overload, visual noise, confusing hierarchy, feature creep. Name each one.
+2. **Edit ruthlessly**. Remove what is not essential. Combine what can be combined. Hide what can wait. Consolidate variation into a single treatment. Commit to a single visual language.
+
+The principle: simplicity is not about fewer features. It is about fewer obstacles between users and their goals. Every element on the page has to justify its existence.
+
+## Try it
+
+```
+/distill this dashboard
+```
+
+Before: four card styles, three button variants, two header treatments, a sidebar with 14 items grouped into 5 sections.
+
+After a `/distill` pass, typical changes:
+
+- Collapse the four card styles into one
+- Pick one button variant, demote the others to text links
+- Unify the headers
+- Group the sidebar into 3 sections, not 5
+- Hide advanced options behind a disclosure
+
+Fewer things. Each one clearer.
+
+## Pitfalls
+
+- **Confusing distill with delete.** Distill removes obstacles. It does not remove features users need. If a user relies on something daily, find a way to keep it quietly, not a way to cut it.
+- **Running it too early.** If the feature is still growing, distilling it now means distilling the same thing again next week. Wait until the shape is stable.
+- **Expecting it to replace hierarchy work.** Sometimes the right fix is not removing things, it is arranging them. Reach for `/layout` when the problem is layout, not quantity.

content/site/skills/extract.md 🔗

@@ -0,0 +1,43 @@
+---
+tagline: "Pull reusable components, tokens, and patterns into the design system."
+---
+
+## When to use it
+
+`/extract` is for the moment your codebase has accidentally become a design system. Repeated button styles in 12 places. Three variants of the same card. Hex colors scattered throughout. Hand-rolled spacing that accidentally matches a scale. Reach for it when you want to consolidate this drift into reusable primitives.
+
+Use it after a product has shipped enough features to reveal the patterns. Premature extraction creates abstractions that do not match reality.
+
+## How it works
+
+The skill discovers the design system structure first, then identifies extraction opportunities:
+
+1. **Tokens**: find repeated literal values (colors, spacing, radii, shadows, font sizes). Propose token names, add to the token system, replace usages.
+2. **Components**: find UI patterns that repeat with minor variation (buttons, cards, inputs, modals). Extract into a single component with variants, migrate callers.
+3. **Composition patterns**: find layout or interaction patterns that repeat (form rows, toolbar groups, empty states). Extract into composition primitives.
+4. **Type styles**: find repeated font-size + weight + line-height combinations. Extract into text styles.
+5. **Animation patterns**: find repeated easing, duration, or keyframe combinations. Extract into motion tokens.
+
+The skill is cautious. It only extracts things used three or more times, with the same intent. It never extracts "because it might be reused later". Premature abstraction is worse than duplication.
+
+## Try it
+
+```
+/extract the button styles
+```
+
+Expected output:
+
+- Found 14 button instances across 8 files
+- 4 distinct variants: primary (filled accent), secondary (bordered), ghost (text-only), destructive (filled red)
+- All 4 variants use the same size scale (small, default, large)
+- Extracted into `<Button variant="primary" size="default">` with token-driven styles
+- Migrated 14 call sites, removed ~180 lines of duplicated CSS
+- Added 3 missing tokens: `--button-radius`, `--button-padding-y`, `--button-padding-x`
+
+## Pitfalls
+
+- **Extracting too early.** Two usages are not a pattern. Three might be. Wait until the pattern is obvious.
+- **Over-generalizing.** The extracted component should match the current use cases closely, not anticipate every possible future one. You can always add variants later.
+- **Forgetting the migration.** Extraction without migration leaves the old duplicated code around and creates a third way of doing the same thing. Always migrate in the same pass.
+- **Extracting things that differ in intent.** Two buttons that look similar but serve different purposes (primary action vs link styled as button) should probably stay separate.

content/site/skills/harden.md 🔗

@@ -0,0 +1,45 @@
+---
+tagline: "Make interfaces production-ready. Edge cases, onboarding, i18n, error states, overflow."
+---
+
+## When to use it
+
+`/harden` is for the day your interface meets reality. Real user data is messy: names that are 60 characters long, product titles in German, prices in the billions, empty lists, 500 errors, offline modes, right-to-left text. Designs that only work with perfect data are not production-ready.
+
+Reach for it before launch, before opening to a new market, or any time a bug report starts with "our user had a really long name and".
+
+## How it works
+
+The skill works through five dimensions of real-world resilience:
+
+1. **Text and data extremes**. Long text, short text, special characters, emoji, RTL, numbers in the billions, 1000-item lists, zero-data empty states.
+2. **Error scenarios**. Network failures, API 4xx/5xx, validation errors, permission errors, rate limits, concurrent operations.
+3. **Internationalization**. Long translations (German is often 30% longer than English), RTL languages, date and number formats, currency symbols, character sets.
+4. **Onboarding and empty states**. First-run experiences, empty state design, progressive disclosure, feature discovery. Making the feature work for someone who has never seen it before.
+5. **Device and context**. Touch targets, offline behavior, slow connections, low-power mode.
+
+For each dimension it identifies the failure mode, then applies the concrete fix: overflow handling, proper empty states, informative error UI, i18n-safe layouts, pluralization, sensible fallbacks.
+
+## Try it
+
+Start with one page and one dimension:
+
+```
+/harden the user profile page for long names
+```
+
+Expected output:
+
+- `.user-name` now has `text-overflow: ellipsis` with a tooltip for the full value
+- `.bio` switched from fixed height to `max-height` with a "show more" disclosure
+- Added an empty state for users with no bio
+- Added a skeleton loader for the async avatar fetch
+- Tested at name lengths 1, 20, 60, 200 characters
+
+Run it per-page, not all at once. The first run is the biggest; subsequent runs find fewer issues as patterns solidify.
+
+## Pitfalls
+
+- **Waiting for a bug report.** Harden is preventative. If you find yourself fixing the same class of bug twice, run `/harden` across the feature.
+- **Treating error and empty states as an afterthought.** Most hardening work is error and empty state UI. Budget time for it, not just a `catch` block.
+- **Skipping i18n because "we are English-only for now".** i18n-safe layouts are still better layouts. Flexible containers, proper text wrapping, generous line-height. None of that hurts English.

content/site/skills/impeccable.md 🔗

@@ -0,0 +1,51 @@
+---
+tagline: "The design intelligence behind every other skill."
+---
+
+## When to use it
+
+`/impeccable` is the foundation. It teaches your AI harness how to design, period. Every other command in this pack leans on it for design principles, anti-patterns, typography, color, and layout guidance.
+
+Call `/impeccable` directly when you want freeform design with the full guidebook loaded. Or use one of the two sub-modes:
+
+### /impeccable craft {#craft}
+
+The full shape-then-build flow. It starts by running `/shape` internally (a structured discovery interview about purpose, audience, and goals), then moves into implementation with visual iteration, checking the result in the browser until the polish is high. Best for brand-new features where you want to think before you build, without managing the steps yourself.
+
+### /impeccable teach {#teach}
+
+One-time project setup. Runs a short discovery interview about your brand, audience, and aesthetic direction, then writes a `.impeccable.md` file that every future skill call reads automatically. Run this once per project before doing any design work.
+
+### /impeccable extract {#extract}
+
+Pull reusable components, design tokens, and patterns out of your code and into the design system. Finds repeated UI patterns (buttons in 12 places, three card variants, scattered hex colors), extracts them into shared primitives, and migrates all callers. Best used after a product has shipped enough features to reveal the patterns -- premature extraction creates abstractions that do not match reality.
+
+## How it works
+
+Most AI-generated UIs fail the same way: generic fonts, purple gradients, card grids on card grids, glassmorphism everywhere. `/impeccable` gives your AI a strong point of view. It loads an opinionated design handbook plus a long list of anti-patterns, then pushes the model to commit to a specific aesthetic direction before writing a single line of code.
+
+The skill has a **Context Gathering Protocol** built in. It will not design anything until it knows who uses the product, what they're trying to do, and how the interface should feel. If no context exists yet, it asks you to run `/impeccable teach` first. This is deliberate: design without context produces slop, and slop is the whole problem this pack exists to solve.
+
+## Try it
+
+From a clean project, run once:
+
+```
+/impeccable teach
+```
+
+Answer the discovery questions. The skill writes a `.impeccable.md` file with your brand, audience, and aesthetic direction. Every future skill call reads it automatically.
+
+Then build something:
+
+```
+/impeccable build me a pricing page for a developer tool
+```
+
+You should get a page that commits to one clear aesthetic direction, uses non-default fonts, avoids the AI color palette, and has a real point of view.
+
+## Pitfalls
+
+- **Skipping `/impeccable teach`.** Without a `.impeccable.md` file, the skill has to ask you context questions mid-flight. Faster to set it up once.
+- **Treating it like a style guide.** It is an opinionated design partner, not a linter. The defaults exist to raise the floor, not to overrule your judgment. If you have a real reason to push back (brand guideline, accessibility constraint, user research that says otherwise), push back and explain why. The skill will work with you. What produces worse output is ignoring the opinion without a reason.
+- **Expecting it to fix existing code.** For that, reach for `/polish`, `/distill`, or `/critique` instead. `/impeccable` is for creation.

content/site/skills/layout.md 🔗

@@ -0,0 +1,41 @@
+---
+tagline: "Fix layout, spacing, and visual rhythm."
+---
+
+## When to use it
+
+`/layout` is for pages where nothing is technically wrong but nothing is breathing either. Equal padding everywhere, monotonous card grids, content that runs edge to edge, hierarchy that relies on size alone. Reach for it when a layout "feels off" and you cannot articulate why.
+
+Good triggers: "everything feels crowded", "it reads like a wall", "I do not know where to look first".
+
+## How it works
+
+The skill runs through five layout dimensions:
+
+1. **Spacing**: is the spacing scale consistent or are there random 13px gaps, are related elements grouped tightly with generous space between groups, is there any rhythm at all.
+2. **Visual hierarchy**: does the eye land on the primary action within 2 seconds, is the hierarchy doing real work or is everything shouting.
+3. **Grid and structure**: is there an underlying grid or is the layout random, are elements aligned to baselines.
+4. **Rhythm**: does the page alternate between tight and generous spacing, or is everything uniform.
+5. **Density**: is the layout cramped or is it wasteful, does density match the content type.
+
+Fixes usually involve rebuilding the spacing scale, introducing asymmetry, collapsing monotonous grids into a mixed layout with hero and supporting elements, and giving the primary action real space.
+
+## Try it
+
+```
+/layout the settings page
+```
+
+Typical changes:
+
+- Spacing scale unified to 8 / 16 / 24 / 48 / 96px
+- Section breaks at 48px, row gaps at 16px, form field groups at 8px
+- Primary actions pulled out of the form flow with 32px buffer
+- Decorative borders removed, replaced with spacing-driven grouping
+- Sidebar and main column proportions rebalanced (280 / flex vs 25 / 75)
+
+## Pitfalls
+
+- **Confusing arrange with distill.** If the problem is too many things, run `/distill` first. Layout is for arranging what is already the right set.
+- **Expecting it to rescue a broken grid.** If the page has no grid at all, arrange will build one. Just know that the diff is going to be larger than you expect.
+- **Ignoring the hierarchy verdict.** If arrange says "nothing is primary", no amount of spacing work fixes that. You need a content decision, not a layout tweak.

content/site/skills/normalize.md 🔗

@@ -0,0 +1,41 @@
+---
+tagline: "Pull drifted UI back into the design system."
+---
+
+## When to use it
+
+`/normalize` is for the page that looks almost right but is not. Hard-coded colors where tokens should be, one-off spacing, a custom button that should have been the shared button, component markup that drifted during a rushed feature. Use it when consistency has decayed and you want to pull the feature back in line with the design system.
+
+Good triggers: "this page uses a different button", "the spacing feels wrong compared to the rest of the app", "why is this blue different from the other blue".
+
+## How it works
+
+The skill audits the target against the design system, then fixes the drift:
+
+1. **Tokens**: find every hard-coded color, spacing, radius, or shadow. Replace with the right token. Flag cases where no token fits (usually a sign the token system needs extending).
+2. **Components**: find custom implementations of things the library already provides. Replace with the shared component, preserving functionality.
+3. **Patterns**: find layouts that deviate from the standard patterns (form layouts, card grids, page headers). Align with the convention.
+4. **Spacing and rhythm**: align to the spacing scale, fix any random pixel values.
+5. **Typography**: make sure type styles come from the system, not one-off font-size/weight combinations.
+
+Normalize is conservative. It does not redesign. It makes the feature match what the rest of the app already does.
+
+## Try it
+
+```
+/normalize the billing page
+```
+
+Typical diff:
+
+- 14 hard-coded hex values replaced with `var(--color-*)` tokens
+- 6 one-off spacing values (13px, 27px, 11px) aligned to 8 / 16 / 24 / 32
+- Custom "Upgrade" button replaced with `<Button variant="primary">` from the shared library
+- Form layout restructured to use the shared `FormRow` component
+- Font weight for section headings unified (was mixing 500 and 600 across similar headings)
+
+## Pitfalls
+
+- **Using normalize to redesign.** If you find yourself wanting to change how things look, not just make them consistent, you want `/polish` or `/arrange`. Normalize enforces consistency with what exists.
+- **Skipping the token gap analysis.** When normalize flags "no token fits", that is signal. Extend the token system before shipping, or the drift will come back.
+- **Running normalize on a prototype.** If the feature is still exploring, consistency is premature optimization. Normalize ships-ready features.

content/site/skills/onboard.md 🔗

@@ -0,0 +1,40 @@
+---
+tagline: "Design first-run experiences, empty states, and paths to value."
+---
+
+## When to use it
+
+`/onboard` is for the moments that decide whether a new user sticks around: the first screen, the empty state, the setup flow, the product tour, the "what do I do now" gap. Reach for it when activation is weak, when new users drop off before reaching value, or when your product has empty states that say "no items yet" and stop there.
+
+## How it works
+
+The skill starts from one question: what is the aha moment, and how fast can a new user get there. Every design decision points at that moment.
+
+It works across the surfaces that shape first impressions:
+
+1. **First-run experience**: the moments immediately after sign-up. Should the user see a tour, a blank canvas, a filled example, or nothing at all. Pick the approach that matches the product.
+2. **Empty states**: every zero-data screen gets oriented. Where am I, why is this empty, what do I do next, what will it look like once it is full.
+3. **Setup and installation**: required configuration is minimized, defaults are smart, each step explains why it matters.
+4. **Progressive disclosure**: advanced features stay out of the way until they are earned.
+5. **Activation events**: the moment a user first experiences the core value is instrumented and celebrated, quietly.
+
+The skill resists two common failure modes: over-tutorialized onboarding where users click through a carousel before they can touch anything, and zero-onboarding where users are dropped into an empty app and expected to figure it out.
+
+## Try it
+
+```
+/onboard the editor
+```
+
+Typical output:
+
+- First-run: replaces empty editor with a filled example document the user can modify. Cancel button discards the example, edit replaces the content with the user's work.
+- Empty state on document list: "No documents yet. Create your first, or import from Notion, Google Docs, or Markdown."
+- Setup: reduced from 6 required fields to 1 (workspace name). Everything else has a smart default and can be edited later in settings.
+- Activation: the first time a user saves a document, a quiet toast says "Saved. Your work is in the cloud now." One-time, not repeated.
+
+## Pitfalls
+
+- **Adding a product tour as the default answer.** Most products do not need a tour. They need a better first screen. Tours are a crutch.
+- **Designing onboarding without defining the aha moment.** If you cannot say in one sentence what the user should feel in the first 60 seconds, go back to `/shape` first.
+- **Running onboard on a broken flow.** Fix the flow first. Onboarding cannot rescue a product where the core action is broken.

content/site/skills/optimize.md 🔗

@@ -0,0 +1,56 @@
+---
+tagline: "Diagnose and fix UI performance from LCP to bundle size."
+---
+
+## When to use it
+
+`/optimize` is for interfaces that feel slow. First paint takes forever, scrolling janks, images pop in late, interactions feel laggy, the bundle ships 800KB of JavaScript. Use it when the Web Vitals are bad or when users are complaining that things are sluggish.
+
+Do not use it as premature optimization. If LCP is 1.1s and INP is 80ms, stop. The design work matters more.
+
+## How it works
+
+The skill works through five perf dimensions:
+
+1. **Loading and Web Vitals**: LCP, INP, CLS. Identify what is blocking the first paint, what is delaying interaction, what is shifting layout.
+2. **Rendering**: unnecessary re-renders, missing memoization, expensive reconciliation, layout thrash in loops.
+3. **Animations**: is anything animating layout properties, are transforms and opacity the only thing touched, does `will-change` help or hurt here.
+4. **Images and assets**: lazy loading, responsive images (`srcset`, `sizes`), modern formats (WebP, AVIF), dimensions set to prevent CLS.
+5. **Bundle size**: unused imports, oversized dependencies, missing code-splitting, dead code.
+
+The skill measures before and after. Every fix gets quantified. If a change does not move a metric, it gets rolled back.
+
+## Try it
+
+```
+/optimize the homepage
+```
+
+Expected shape:
+
+```
+LCP: 3.2s → 1.4s
+  - Hero image preloaded (-800ms)
+  - Removed render-blocking font stylesheet (-240ms)
+  - Deferred analytics script (-180ms)
+
+INP: 240ms → 90ms
+  - Debounced scroll handler
+  - Memoized expensive list render
+  - Removed synchronous layout read in event loop
+
+CLS: 0.18 → 0.02
+  - Set dimensions on hero image and logo
+  - Reserved space for async header badge
+
+Bundle: 340KB → 180KB
+  - Removed unused lodash import (52KB)
+  - Code-split the playground route (78KB)
+  - Dropped deprecated icon set (30KB)
+```
+
+## Pitfalls
+
+- **Optimizing before measuring.** Without baseline metrics, you cannot tell what helped. Run `/optimize` with specific Web Vitals numbers, not vibes.
+- **Chasing tiny wins.** A 20ms improvement in INP that takes a week is rarely worth it. Optimize has diminishing returns; know when to stop.
+- **Forgetting to re-measure after every change.** The build could have made things worse in a way the skill did not predict. Verify.

content/site/skills/overdrive.md 🔗

@@ -0,0 +1,30 @@
+---
+tagline: "Push an interface past conventional limits. Shaders, physics, 60fps, cinematic transitions."
+---
+
+## When to use it
+
+`/overdrive` is for the moments where you want to impress. A hero that uses WebGL. A table that handles a million rows. A dialog that morphs out of its trigger element. A form that validates in real-time with streaming feedback. A page transition that feels cinematic. Use it when the project budget allows for technical ambition and the outcome needs to feel extraordinary.
+
+Do not use it on operator tools, dashboards, or anything where reliability beats spectacle. Overdrive burns complexity for effect, and that trade-off is only worth it on moments that matter.
+
+## How it works
+
+The skill picks one moment to make extraordinary and commits to it, rather than spreading effort across the whole interface. It then reaches for techniques most AI-generated UIs never touch: WebGL shaders, spring physics, Scroll Timeline, View Transitions, canvas animation, GPU-accelerated filters. Everything is budgeted, profiled, and tested at 60fps, with reduced-motion fallbacks baked in.
+
+Overdrive output is announced with `──── ⚡ OVERDRIVE ────` so you know you are entering a more ambitious mode. Expect larger diffs, new dependencies, and implementation depth beyond what other skills produce.
+
+## Try it
+
+```
+/overdrive the landing hero
+```
+
+One concrete run might replace a static hero with a WebGL shader background driven by mouse position, a display headline that reveals with a mask on scroll using the Scroll Timeline API, and a View Transition on the CTA that morphs into the next page. Plus a reduced-motion fallback that swaps all of it for a clean static composition.
+
+## Pitfalls
+
+- **Using it everywhere.** Overdrive works because it is rare. If every page has cinematic moments, none of them are cinematic.
+- **Shipping without reduced-motion fallbacks.** Non-negotiable. Overdrive adds them automatically; do not remove them.
+- **Ignoring performance.** Extraordinary moments still need to hit 60fps. If the effect drops frames, cut it or optimize. Slow spectacle is worse than simple done well.
+- **Running overdrive before the base interface is solid.** Spectacle on a broken foundation reads as distraction, not delight.

content/site/skills/polish.md 🔗

@@ -0,0 +1,46 @@
+---
+tagline: "The meticulous final pass between good and great."
+---
+
+## When to use it
+
+`/polish` is the last thing you run before shipping. It hunts down the small details that separate a shipped feature from a polished one: half-pixel misalignments, inconsistent spacing, forgotten focus states, loading transitions that flash, copy that drifts in tone. It also aligns the feature with your design system -- replacing hard-coded values with tokens, swapping custom components for shared ones, and fixing any drift from established patterns.
+
+Reach for it when the feature is functionally complete, nothing is broken, and something still feels off. Also reach for it when a feature has drifted from the design system and needs to be pulled back in line.
+
+## How it works
+
+Polish starts by discovering the design system (tokens, spacing scale, shared components), then works methodically across six dimensions:
+
+1. **Visual alignment and spacing**: pixel-perfect grid adherence, consistent spacing scale, optical alignment on icons.
+2. **Typography**: hierarchy consistency, line length, widows and orphans, kerning on headlines.
+3. **Color and contrast**: token usage, theme parity, WCAG ratios, focus indicators.
+4. **Interaction states**: hover, focus, active, disabled, loading, error, success. Every state accounted for.
+5. **Transitions and motion**: smooth easing, no layout jank, respect for `prefers-reduced-motion`.
+6. **Copy**: consistent voice, correct tense, no placeholder strings, no stray TODOs.
+
+The skill is explicit about one thing: polish is the last step, not the first. If the feature is not functionally complete, polishing it is wasted work.
+
+## Try it
+
+```
+/polish the pricing page
+```
+
+A healthy run looks like:
+
+```
+Visual alignment: fixed 3 off-grid elements (8px baseline)
+Typography: tightened h1 kerning, fixed widow on testimonial
+Interaction: added hover state on FAQ items, focus ring on email input
+Motion: softened modal entrance, added reduced-motion fallback
+Copy: removed one "Lorem ipsum" stray, aligned button voice
+```
+
+Five small fixes, no rewrites. That is the shape of a good polish pass.
+
+## Pitfalls
+
+- **Polishing work that is not done.** If there are TODOs in the code, you are not ready. Run `/polish` on finished features only.
+- **Treating polish as redesign.** Polish refines what exists. If you find yourself rearchitecting a layout, you needed `/critique` or `/layout` instead.
+- **Running `/polish` without `/audit` first.** Polish catches feel-based issues. Audit catches measurable ones. Use both.

content/site/skills/quieter.md 🔗

@@ -0,0 +1,40 @@
+---
+tagline: "Tone down designs that are shouting without losing their intent."
+---
+
+## When to use it
+
+`/quieter` is the counterweight to `/bolder`. Reach for it when an interface is visually aggressive, overstimulating, or trying to do too many things at full volume. Neon on dark, gradient text everywhere, 6 accent colors, everything animated, 20px shadows. Use quieter when the design needs to breathe and you want refinement without losing the point of view.
+
+Also useful after `/bolder` goes a little too far.
+
+## How it works
+
+The skill works by reduction across four axes:
+
+1. **Color**: desaturate, lower chroma in OKLCH, pull accents back to a single primary plus muted support. No more than two intentional colors.
+2. **Contrast**: soften extreme darks and lights, pull the range in. Backgrounds move from pure white and pure black to paper and ink.
+3. **Decoration**: remove shadows that are not doing work, drop borders that are not carrying structure, retire gradients that exist for energy rather than hierarchy.
+4. **Motion and effect**: slow animations down, remove anything that auto-plays, drop parallax and blur unless they serve readability.
+
+The skill preserves the design's intent. If the original had a point of view, the quieter version has the same point of view with more confidence. Refinement, not neutralization.
+
+## Try it
+
+```
+/quieter the pricing page
+```
+
+Typical diff:
+
+- Gradient text on the price removed, replaced with solid ink at one weight heavier
+- Three accent colors reduced to one (magenta), the other two become neutral variants
+- Card shadows reduced from `0 20px 40px rgba(0,0,0,0.2)` to `0 1px 0 var(--color-mist)` (a hairline)
+- Background switches from dark gradient to paper with a subtle cream wash at the top
+- Hero animation from 1.2s easeOut with 3 staggered elements to a single 260ms fade-in
+
+## Pitfalls
+
+- **Over-applying.** Quieter can strip personality if you run it on something that was already measured. Use it when the design is too loud, not when it is correctly assertive.
+- **Confusing quieter with distill.** Quieter reduces intensity. Distill removes elements. They are different moves.
+- **Running it in response to a critique that says "too busy".** Busy usually means too many things, not too loud. Try `/distill` first.

content/site/skills/shape.md 🔗

@@ -0,0 +1,40 @@
+---
+tagline: "Think before you build. Produce a design brief through discovery, not guesswork."
+---
+
+## When to use it
+
+`/shape` is where a feature starts. Before anyone writes code, before anyone argues about the hero treatment, before anyone picks a font. Use it to force a discovery conversation about purpose, users, content, and constraints, then capture the answers as a design brief the implementation skills can lean on.
+
+Reach for it whenever a feature is about to start, a ticket is vague, or you catch yourself writing JSX to figure out what the product should be.
+
+## How it works
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. The model jumps to "here is a card grid" without asking "what is the user trying to accomplish". `/shape` inverts that order.
+
+The skill runs a structured discovery interview in conversation. It will not write code during this phase. The questions cover:
+
+- **Purpose and context**: what the feature is for, who uses it, what state of mind they are in
+- **Content and data**: what is displayed, realistic ranges, edge cases, what is dynamic
+- **Design goals**: the single most important thing, the intended feeling, reference examples
+- **Constraints**: technical, content, accessibility, localization
+
+You answer naturally. The skill asks follow-ups, not a form. At the end it produces a design brief: a structured artifact you can hand to `/impeccable` or any other implementation skill.
+
+Note: if you want the full flow -- discovery interview, then straight into building -- use `/impeccable craft` instead. It runs `/shape` internally, then continues into implementation with visual iteration. `/shape` standalone is for when you want just the brief, so you can take it to whatever implementation approach you prefer.
+
+## Try it
+
+```
+/shape a daily digest email preferences page
+```
+
+Expect a 5 to 10 question conversation. The skill asks things like "who is the person opening this, and are they already committed or still curious" and "what happens when the user has unsubscribed from everything, do we hide the feature or show something". You answer, and a brief materializes.
+
+From there you can hand the brief to `/impeccable`, `/polish`, or any other skill. Or just use it as a reference while you build by hand.
+
+## Pitfalls
+
+- **Skipping it because it feels slow.** The interview is maybe 5 minutes. The rewrites you avoid are measured in hours.
+- **Treating the brief as a spec.** It is a compass, not a checklist. It captures intent, not UI.
+- **Answering with "standard" or "normal".** Specificity is the whole point. If a user is "rushed, on mobile, between meetings", say so. That changes everything downstream.

content/site/skills/typeset.md 🔗

@@ -0,0 +1,42 @@
+---
+tagline: "Fix typography that feels generic, inconsistent, or accidental."
+---
+
+## When to use it
+
+Reach for `/typeset` when the text on a page looks like default typography instead of designed typography. Muddy hierarchy, three sizes that look the same, body copy at 14px, a display font that is actually just Inter bold, headlines with no kerning attention.
+
+Common triggers: "hierarchy feels flat", "readability is off", "fonts look generic".
+
+## How it works
+
+The skill assesses typography across five dimensions:
+
+1. **Font choices**: are you using invisible defaults (Inter, Roboto, Arial, Open Sans), does the typeface match the brand, are there more than 2 to 3 families.
+2. **Hierarchy**: are heading, body, and caption clearly different at a glance, is the size contrast at least 1.25x between steps, are weight contrasts legible.
+3. **Sizing and scale**: is there a coherent type scale, does body text meet 16px minimum, is the scale fixed-rem for app UIs or fluid-clamp for marketing pages.
+4. **Readability**: line length 45 to 75 characters, line-height tuned for font and context, contrast.
+5. **Consistency**: same element uses same treatment everywhere, no one-off font-size overrides.
+
+It then fixes what it finds: picks distinctive typefaces, builds a modular scale, widens hierarchy contrast, sets proper line length and leading.
+
+## Try it
+
+```
+/typeset the article layout
+```
+
+Expected diff:
+
+- Display font swapped from Inter 700 to a real display face
+- Type scale rebuilt: 3rem / 2rem / 1.25rem / 1rem / 0.875rem, ratio 1.333
+- Body text bumped from 14px to 16px
+- Line length clamped to 68ch on the article column
+- Line-height 1.6 for body, 1.1 for display
+- Removed four one-off `font-size` values scattered in component styles
+
+## Pitfalls
+
+- **Asking for a new font without context.** Typeset will pick based on the `.impeccable.md` brand voice. If you have not run `/impeccable teach`, the suggestion will be generic.
+- **Reaching for typeset when the issue is layout.** If paragraphs are fine but the page feels cramped, you want `/layout`.
+- **Expecting fluid clamp scales on app UIs.** Typeset uses fixed rem scales for app interfaces. Fluid typography is for marketing and content pages where line length varies dramatically.

content/site/tutorials/critique-with-overlay.md 🔗

@@ -0,0 +1,129 @@
+---
+title: Critique with the visual overlay
+tagline: "Use /critique plus the browser overlay to review a live page with ground truth."
+order: 2
+description: "Run a full design critique that combines LLM assessment, the automated detector, and a live browser overlay so you can see exactly which elements trigger which anti-patterns on the page you're looking at."
+---
+
+## What you'll build
+
+You will run a complete design critique against a live page in your browser, with every flagged anti-pattern highlighted directly on the element that caused it. No screenshots, no guesswork, no paragraph of findings you have to map back to the code.
+
+Total time: about ten minutes.
+
+## Prerequisites
+
+- Impeccable installed in your project (see [getting started](/tutorials/getting-started) if you have not).
+- A harness with browser automation available (Claude Code with the Chrome extension, or similar).
+- A page you want to critique, either local (`localhost:3000/pricing`) or deployed.
+
+## Step 1. Run /critique
+
+From your harness, run:
+
+```
+/critique the pricing page at localhost:3000/pricing
+```
+
+The skill kicks off two independent assessments in parallel. They run in separate sub-agents so one does not bias the other.
+
+### What the LLM assessment does
+
+The first assessment reads your source code and, if browser automation is available, opens the live page in a new tab. It walks the full impeccable skill DO/DON'T catalog and scores the page against Nielsen's 10 heuristics, the 8-item cognitive load checklist, and the brand fit from your `.impeccable.md`.
+
+It labels the tab it opens with `[LLM]` in the title so you can tell which one is which.
+
+### What the automated detector does
+
+The second assessment runs `npx impeccable detect` against the page. This is deterministic: 25 specific pattern checks that fire or do not fire. Gradient text, purple palettes, side-tab borders, nested cards, line length problems, low contrast, tiny body text, and the rest.
+
+You get back a JSON list of every finding with its element selector, the rule that fired, and a short description.
+
+## Step 2. Open the visual overlay
+
+Impeccable ships with a visual mode that highlights every detected anti-pattern directly on the page. Here is what it looks like running on a deliberately-bad synthwave landing page:
+
+<div class="tutorial-embed">
+  <div class="tutorial-embed-header">
+    <span class="tutorial-embed-dot red"></span>
+    <span class="tutorial-embed-dot yellow"></span>
+    <span class="tutorial-embed-dot green"></span>
+    <span class="tutorial-embed-title">Live detection overlay</span>
+  </div>
+  <iframe src="/antipattern-examples/visual-mode-demo.html" class="tutorial-embed-iframe" loading="lazy" title="Impeccable visual overlay running on a demo page"></iframe>
+</div>
+
+Every outlined element has a floating label naming the rule that fired. Hover an outline to see the full finding. This is exactly what you will see on your own page.
+
+You have three ways to open it:
+
+1. **[Chrome extension](https://chromewebstore.google.com/detail/impeccable/bdkgmiklpdmaojlpflclinlofgjfpabf)**: one-click activation on any page. Click the Impeccable icon in the toolbar and every anti-pattern gets highlighted instantly.
+2. **Inside `/critique`**: the skill opens a browser tab labeled `[Human]` with the detector active during the browser portion of the assessment. You do not need to do anything extra.
+3. **Standalone CLI**: `npx impeccable live` starts a local server that serves the detector script. You inject it into any page by adding a `<script>` tag.
+
+For this tutorial, the easiest option is the Chrome extension. Install it, navigate to your pricing page, and click the Impeccable icon. You will see the overlay appear immediately on the live page.
+
+## Step 3. Merge the two assessments
+
+Back in your harness, `/critique` has finished and produced a combined report. It looks something like:
+
+```
+AI slop verdict: FAIL
+  Detected tells: gradient-text (2), ai-color-palette (1),
+                  nested-cards (1), side-tab (3)
+
+Heuristic scores (avg 2.8/4):
+  Visibility of status: 3 (good)
+  Match between system and real world: 2 (partial)
+  Consistency and standards: 2 (partial)
+  ...
+
+Cognitive load: 3/8 failures (moderate)
+  Visible options at primary decision: 6 (flag)
+  Decision points stacked at top: yes (flag)
+  Progressive disclosure: absent on advanced pricing toggles
+
+What's working:
+  - Clear price hierarchy
+  - Strong headline
+
+Priority issues:
+  1. Hero uses gradient text on the main price
+     Why: AI tell, reduces contrast, hurts scannability
+     Fix: solid ink color at one weight heavier
+  2. Feature comparison table has 4 nested card levels
+     Why: visual noise, unclear hierarchy
+     Fix: flatten to a table with zebra striping
+
+Questions to answer:
+  - Is the free tier a real product or a funnel?
+  - What does a user feel when they land here from an ad vs from search?
+```
+
+## Step 4. Fix the findings
+
+The report gives you a priority list. You can work through them one at a time, ask the model to fix them all at once, or anything in between. What matters is using the overlay to verify:
+
+1. Keep the overlay open in one tab.
+2. Make fixes in code (or ask the model to fix everything).
+3. Reload. The overlay re-scans and resolved findings disappear.
+
+This feedback loop is the reason the overlay matters. You see fixes land in real time, and you never ship a "fix" that did not actually satisfy the rule.
+
+## Step 5. Re-run /critique when you are done
+
+After you have worked through the priority list, run `/critique` again. The goal is a clean AI slop verdict and at least a 3.5 average on the heuristics. Cognitive load should be below 2 failures.
+
+If something still fires, fix it or write a suppression comment explaining why the rule does not apply in your context (the detector respects a small set of opt-out pragmas, but use them sparingly).
+
+## What to try next
+
+- `/audit the same page` to catch the implementation issues critique does not cover (accessibility, performance, theming).
+- `/polish` if the critique report is clean and you want the last-mile refinement pass.
+- `/distill` if critique flagged "too busy" or "cognitive load". Distill removes what should not be there.
+
+## Common issues
+
+- **The overlay shows no findings but critique says there are problems**. The detector catches deterministic patterns. Critique catches judgment calls. They are complementary, not redundant.
+- **The LLM assessment and the detector disagree**. That is normal. The LLM is subjective. The detector is deterministic. When they disagree, look at both and make a call.
+- **The overlay breaks the page layout**. Rare, but some CSS can interact with the injected overlay styles. Use the [Chrome extension](https://chromewebstore.google.com/detail/impeccable/bdkgmiklpdmaojlpflclinlofgjfpabf) for the most reliable experience, or run `npx impeccable detect` from the CLI and apply findings manually.

content/site/tutorials/getting-started.md 🔗

@@ -0,0 +1,82 @@
+---
+title: Getting started
+tagline: "From zero to your first /polish pass in five minutes."
+order: 1
+description: "Install Impeccable, run /impeccable teach once to establish project context, and run /polish on something that already exists. The fastest path to seeing what Impeccable changes about AI-generated design."
+---
+
+## What you'll build
+
+You will end this tutorial with Impeccable installed in your project, a `.impeccable.md` file that captures your brand and audience, and one hand-polished page that went through a `/polish` pass. Total time: about five minutes.
+
+## Prerequisites
+
+- An AI coding harness: Claude Code, Cursor, Gemini CLI, Codex CLI, or any of the other supported tools.
+- A project with at least one HTML or component file you want to improve. A fresh scaffolded landing page works fine.
+
+## Step 1. Install
+
+From the root of your project, run:
+
+```
+npx skills add pbakaus/impeccable
+```
+
+This auto-detects your harness and writes the skill files to the right location (e.g., `.claude/skills/`, `.cursor/skills/`). Reload your harness and type `/`. You should see `/impeccable`, `/polish`, `/critique`, and the other commands in the autocomplete.
+
+## Step 2. Teach Impeccable about your project
+
+This is the most important step. Design without context produces generic output. The `/impeccable teach` command runs a short discovery interview and writes a `.impeccable.md` file at the root of your project.
+
+Run:
+
+```
+/impeccable teach
+```
+
+The skill will ask you a handful of questions:
+
+- **Who is this product for?** Be specific. Not "users" but "solo founders evaluating a new tool on their phone between meetings".
+- **What is the brand voice in three words?** Pick real words. "Warm and mechanical and opinionated" is better than "modern and clean".
+- **What should the interface feel like?** Concrete adjectives. "Calm, trustworthy, fast" or "playful, bold, a little chaotic".
+- **Any visual references?** Screenshots, sites, design systems you admire.
+- **Anti-references?** Things the product should explicitly not look like.
+
+Answer in your own words. The skill writes a `.impeccable.md` file with the answers. Every future skill call reads it automatically.
+
+Open `.impeccable.md` and read what it wrote. Edit anything that does not feel right. The file is yours.
+
+## Step 3. Polish something
+
+Pick a page that already exists. An about page, a settings screen, a pricing table, anything. Run:
+
+```
+/polish the pricing page
+```
+
+The skill will walk through alignment, spacing, typography, color, interaction states, transitions, and copy. It makes targeted fixes, not a rewrite. Expect a handful of small diffs that together lift the page from "done" to "done well".
+
+A typical polish pass looks like:
+
+```
+Visual alignment: fixed 3 off-grid elements
+Typography: tightened h1 kerning, fixed widow on feature list
+Color: replaced one hardcoded hex with --color-accent token
+Interaction: added missing hover state on FAQ items
+Motion: softened modal entrance to 220ms ease-out-quart
+Copy: removed stray 'Lorem' placeholder
+```
+
+Review the diff. If something does not feel right, ask the model to explain the change. If it still does not feel right, revert it. Impeccable is opinionated but not infallible.
+
+## What to try next
+
+- `/critique the landing page` runs a full design review with scoring, persona tests, and automated detection. It is the best way to find what to fix next.
+- `/audit the checkout` runs accessibility, performance, theming, responsive, and anti-pattern checks against the implementation. Useful before shipping.
+- `/impeccable craft a pricing page for enterprise customers` runs the full shape-then-build flow on a brand new feature.
+
+## Common issues
+
+- **The skill says "no design context found"**. You skipped step 2. Run `/impeccable teach` first.
+- **Commands do not appear in the harness**. Reload the harness after installing. If they still do not appear, check that the installer wrote files into the expected location (`.claude/skills/`, `.cursor/skills/`, etc.) and that your harness is picking up that directory.
+- **The polish pass rewrote something you liked**. Say so. Revert the change, tell the model which specific edit to undo, and continue from there.

extension/STORE_LISTING.md 🔗

@@ -0,0 +1,63 @@
+# Chrome Web Store Listing
+
+## Name
+Impeccable
+
+## Short description (132 chars max)
+Detect AI slop and design anti-patterns in any web page. Open DevTools and see what needs fixing.
+
+## Detailed description
+
+Impeccable detects 24 common UI anti-patterns directly in your browser. Open DevTools on any page and overlays instantly highlight issues, from AI-generated design tells to accessibility and quality problems.
+
+WHAT IT DETECTS
+
+AI slop (design tells that scream "AI made this"):
+- Side-tab accent borders
+- Gradient text on headings
+- Purple/violet AI color palettes
+- Nested cards, monotonous spacing
+- Bounce/elastic easing
+- Dark mode with glowing accents
+- Overused fonts, flat type hierarchy
+
+Quality issues (general design and accessibility):
+- Low contrast text (WCAG AA)
+- Cramped padding, tight line height
+- Skipped heading levels
+- Line length too long
+- Tiny body text, justified text
+- Layout property animations
+
+HOW IT WORKS
+
+1. Install the extension
+2. Open DevTools on any page (Cmd+Opt+I / F12)
+3. Overlays appear automatically, highlighting issues
+4. Click the "Impeccable" panel tab for a structured list of all findings
+5. Click any finding to jump to the element in the Elements panel
+
+FEATURES
+
+- Auto-scans when DevTools opens, no manual step needed
+- Grouped findings: AI tells vs. quality issues
+- Click-to-inspect: jump from a finding to the element
+- Toggle overlays on/off from the panel or toolbar popup
+- Per-rule settings: disable detections you don't care about
+- Re-scans on navigation, including SPA route changes
+- Works on any website
+- Runs 100% locally, no data sent anywhere
+
+Open source at https://github.com/pbakaus/impeccable
+
+## Category
+Developer Tools
+
+## Language
+English
+
+## Privacy policy URL
+https://impeccable.style/privacy
+
+## Single purpose description
+Detects and highlights UI anti-patterns (AI-generated design tells and general quality issues) on any web page.

extension/background/service-worker.js 🔗

@@ -0,0 +1,263 @@
+/**
+ * Impeccable DevTools Extension - Service Worker
+ *
+ * Routes messages between popup, DevTools panel, and content scripts.
+ * Maintains per-tab state and updates the badge.
+ */
+
+// Per-tab state: { tabId: { findings, overlaysVisible, injected } }
+const tabState = new Map();
+
+// Active DevTools panel connections: { tabId: Set<port> }
+const panelPorts = new Map();
+
+function getState(tabId) {
+  if (!tabState.has(tabId)) {
+    tabState.set(tabId, { findings: [], overlaysVisible: true, injected: false, csInjected: false });
+  }
+  return tabState.get(tabId);
+}
+
+function updateBadge(tabId) {
+  const state = tabState.get(tabId);
+  const count = state?.findings?.length || 0;
+  const text = count > 0 ? String(count) : '';
+  chrome.action.setBadgeText({ text, tabId }).catch(() => {});
+  chrome.action.setBadgeBackgroundColor({ color: '#d6336c', tabId }).catch(() => {});
+}
+
+function notifyPanels(tabId, message) {
+  const ports = panelPorts.get(tabId);
+  if (ports) {
+    for (const port of ports) {
+      try { port.postMessage(message); } catch { /* port disconnected */ }
+    }
+  }
+}
+
+async function getSettings() {
+  return chrome.storage.sync.get({
+    disabledRules: [],
+    lineLengthMode: 'strict', // 'strict' = 80, 'lax' = 120
+    spotlightBlur: true,      // dim/blur the page on hover-highlight
+    autoScan: 'panel',        // 'panel' = scan when Impeccable UI opens, 'devtools' = scan when DevTools opens
+  });
+}
+
+async function buildScanConfig() {
+  const { disabledRules, lineLengthMode, spotlightBlur } = await getSettings();
+  const config = {};
+  if (disabledRules.length) config.disabledRules = disabledRules;
+  config.lineLengthMax = lineLengthMode === 'lax' ? 120 : 80;
+  config.spotlightBlur = spotlightBlur;
+  return config;
+}
+
+// Inject the content script on-demand. We removed the static content_scripts entry to
+// minimize the always-on footprint; the script is only loaded when the user explicitly
+// engages with the extension (DevTools panel/sidebar opened, popup scan, etc).
+async function ensureContentScriptInjected(tabId) {
+  const state = getState(tabId);
+  if (state.csInjected) return true;
+  try {
+    await chrome.scripting.executeScript({
+      target: { tabId },
+      files: ['content/content-script.js'],
+      injectImmediately: true,
+    });
+    state.csInjected = true;
+    return true;
+  } catch (err) {
+    // Common cause: chrome:// pages, the web store, or other restricted URLs
+    return false;
+  }
+}
+
+async function sendScanToTab(tabId) {
+  const ok = await ensureContentScriptInjected(tabId);
+  if (!ok) return;
+  const config = await buildScanConfig();
+  chrome.tabs.sendMessage(tabId, { action: 'scan', config }).catch(() => {});
+}
+
+// Handle messages from content scripts and popup
+chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
+  const tabId = msg.tabId || sender.tab?.id;
+
+  if (msg.action === 'findings' && tabId) {
+    const state = getState(tabId);
+    state.findings = msg.findings || [];
+    state.injected = true;
+    updateBadge(tabId);
+    notifyPanels(tabId, { action: 'findings', findings: state.findings });
+    // Broadcast for popup
+    chrome.runtime.sendMessage({ action: 'findings-updated', tabId, findings: state.findings }).catch(() => {});
+    sendResponse({ ok: true });
+  }
+
+  else if (msg.action === 'scan' && tabId) {
+    sendScanToTab(tabId);
+    sendResponse({ ok: true });
+  }
+
+  else if (msg.action === 'toggle-overlays' && tabId) {
+    chrome.tabs.sendMessage(tabId, { action: 'toggle-overlays' }).catch(() => {});
+    sendResponse({ ok: true });
+  }
+
+  else if (msg.action === 'page-pointer-active' && tabId) {
+    notifyPanels(tabId, { action: 'page-pointer-active' });
+    sendResponse({ ok: true });
+  }
+
+  else if (msg.action === 'overlays-toggled' && tabId) {
+    const state = getState(tabId);
+    state.overlaysVisible = msg.visible;
+    notifyPanels(tabId, { action: 'overlays-toggled', visible: msg.visible });
+    chrome.runtime.sendMessage({ action: 'overlays-toggled-broadcast', tabId, visible: msg.visible }).catch(() => {});
+    sendResponse({ ok: true });
+  }
+
+  else if (msg.action === 'get-state' && tabId) {
+    sendResponse(getState(tabId));
+  }
+
+  else if (msg.action === 'inject-fallback' && tabId) {
+    // CSP fallback: inject detector via chrome.scripting (bypasses page CSP)
+    chrome.scripting.executeScript({
+      target: { tabId },
+      world: 'MAIN',
+      files: ['detector/detect.js'],
+    }).then(() => {
+      // Detector will post impeccable-ready, content script handles the rest
+    }).catch((err) => {
+      console.warn('[impeccable] Fallback injection failed:', err);
+    });
+    sendResponse({ ok: true });
+  }
+
+  else if (msg.action === 'disabled-rules-changed') {
+    // Re-scan all tabs that have been injected
+    for (const [tid, state] of tabState) {
+      if (state.injected) sendScanToTab(tid);
+    }
+    sendResponse({ ok: true });
+  }
+
+  return true;
+});
+
+// Track which tabs have DevTools open (via the devtools.js lifecycle port)
+const devtoolsTabs = new Set();
+
+async function tearDownTab(tabId) {
+  devtoolsTabs.delete(tabId);
+  // Send the remove command and await it — this keeps the SW alive long enough
+  // to actually deliver the message (setTimeout doesn't survive SW termination in MV3).
+  try {
+    await chrome.tabs.sendMessage(tabId, { action: 'remove' });
+  } catch { /* tab might be closed or content script gone */ }
+  const state = tabState.get(tabId);
+  if (state) {
+    state.findings = [];
+    state.injected = false;
+    state.csInjected = false;
+  }
+  updateBadge(tabId);
+  panelPorts.delete(tabId);
+}
+
+// Handle long-lived connections from DevTools pages and panels
+chrome.runtime.onConnect.addListener((port) => {
+  // Lifecycle port from devtools.js -- tracks DevTools open/close
+  if (port.name.startsWith('impeccable-devtools-')) {
+    const tabId = parseInt(port.name.replace('impeccable-devtools-', ''), 10);
+    devtoolsTabs.add(tabId);
+
+    port.onMessage.addListener((msg) => {
+      if (msg.action === 'scan') sendScanToTab(tabId);
+      // 'ping' is just a keepalive; no action needed
+    });
+
+    port.onDisconnect.addListener(() => {
+      // Tear down immediately — defer with setTimeout doesn't work reliably in MV3
+      // because the SW can be terminated before the timer fires.
+      tearDownTab(tabId);
+    });
+  }
+
+  // Panel port from panel.js -- for forwarding findings/state
+  if (port.name.startsWith('impeccable-panel-')) {
+    const tabId = parseInt(port.name.replace('impeccable-panel-', ''), 10);
+    if (!panelPorts.has(tabId)) panelPorts.set(tabId, new Set());
+    panelPorts.get(tabId).add(port);
+
+    // Send current state to newly connected panel
+    const state = getState(tabId);
+    port.postMessage({ action: 'state', ...state });
+
+    // If no findings yet, the auto-scan from devtools.js may have been lost -- trigger one
+    if (!state.findings.length) {
+      sendScanToTab(tabId);
+    }
+
+    port.onMessage.addListener((msg) => {
+      if (msg.action === 'scan') {
+        sendScanToTab(tabId);
+      } else if (msg.action === 'toggle-overlays') {
+        chrome.tabs.sendMessage(tabId, { action: 'toggle-overlays' }).catch(() => {});
+      } else if (msg.action === 'highlight') {
+        chrome.tabs.sendMessage(tabId, { action: 'highlight', selector: msg.selector }).catch(() => {});
+      } else if (msg.action === 'unhighlight') {
+        chrome.tabs.sendMessage(tabId, { action: 'unhighlight' }).catch(() => {});
+      }
+    });
+
+    port.onDisconnect.addListener(() => {
+      panelPorts.get(tabId)?.delete(port);
+      if (panelPorts.get(tabId)?.size === 0) panelPorts.delete(tabId);
+    });
+  }
+
+  // Sidebar pane port (Elements panel sidebar) -- receives findings updates.
+  // Connecting the sidebar is a strong signal of "user engaged with Impeccable"
+  // so we trigger a scan if no findings exist yet (matches the panel port behavior).
+  if (port.name.startsWith('impeccable-sidebar-')) {
+    const tabId = parseInt(port.name.replace('impeccable-sidebar-', ''), 10);
+    if (!panelPorts.has(tabId)) panelPorts.set(tabId, new Set());
+    panelPorts.get(tabId).add(port);
+
+    const state = getState(tabId);
+    port.postMessage({ action: 'state', ...state });
+    if (!state.findings.length) sendScanToTab(tabId);
+
+    port.onDisconnect.addListener(() => {
+      panelPorts.get(tabId)?.delete(port);
+      if (panelPorts.get(tabId)?.size === 0) panelPorts.delete(tabId);
+    });
+  }
+});
+
+// Re-scan on navigation (only if DevTools is open AND user was actively scanning)
+chrome.webNavigation?.onCompleted?.addListener((details) => {
+  if (details.frameId !== 0) return;
+  if (!devtoolsTabs.has(details.tabId)) return;
+  const state = tabState.get(details.tabId);
+  if (!state) return;
+  // Only re-scan if the user has actively engaged (had findings or injected previously)
+  const wasActive = state.injected || state.findings.length > 0;
+  state.findings = [];
+  state.injected = false;
+  state.csInjected = false; // page reload destroys the content script
+  updateBadge(details.tabId);
+  notifyPanels(details.tabId, { action: 'navigated' });
+  if (wasActive) {
+    setTimeout(() => sendScanToTab(details.tabId), 300);
+  }
+});
+
+// Clean up state when tabs close
+chrome.tabs.onRemoved.addListener((tabId) => {
+  tabState.delete(tabId);
+  panelPorts.delete(tabId);
+});

extension/content/content-script.js 🔗

@@ -0,0 +1,125 @@
+/**
+ * Impeccable DevTools Extension - Content Script
+ *
+ * Bridges between the extension messaging system and the page-context detector.
+ * The detector must run in page context (not isolated world) because it needs
+ * access to getComputedStyle, document.styleSheets.cssRules, etc.
+ *
+ * Wrapped in an IIFE with an idempotency flag so re-injection (via
+ * chrome.scripting.executeScript) is a no-op and doesn't cause:
+ *   - SyntaxError: Identifier 'foo' has already been declared
+ *   - Duplicate event listeners accumulating over time
+ */
+(function () {
+  if (window.__IMPECCABLE_CS_LOADED__) return;
+  window.__IMPECCABLE_CS_LOADED__ = true;
+
+  let injected = false;
+  let pendingScan = false;
+  let scanConfig = null;
+
+  // Listen for commands from the service worker
+  chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
+    if (msg.action === 'scan') {
+      scanConfig = msg.config || null;
+      injectAndScan();
+      sendResponse({ ok: true });
+    } else if (msg.action === 'toggle-overlays') {
+      window.postMessage({ source: 'impeccable-command', action: 'toggle-overlays' }, '*');
+      sendResponse({ ok: true });
+    } else if (msg.action === 'remove') {
+      window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
+      injected = false;
+      sendResponse({ ok: true });
+    } else if (msg.action === 'highlight') {
+      window.postMessage({ source: 'impeccable-command', action: 'highlight', selector: msg.selector }, '*');
+      sendResponse({ ok: true });
+    } else if (msg.action === 'unhighlight') {
+      window.postMessage({ source: 'impeccable-command', action: 'unhighlight' }, '*');
+      sendResponse({ ok: true });
+    }
+    return true;
+  });
+
+  // Listen for results and state changes from the detector in page context
+  window.addEventListener('message', (e) => {
+    if (e.source !== window || !e.data) return;
+
+    if (e.data.source === 'impeccable-results') {
+      chrome.runtime.sendMessage({
+        action: 'findings',
+        findings: e.data.findings,
+        count: e.data.count,
+      }).catch(() => {});
+    }
+
+    if (e.data.source === 'impeccable-overlays-toggled') {
+      chrome.runtime.sendMessage({
+        action: 'overlays-toggled',
+        visible: e.data.visible,
+      }).catch(() => {});
+    }
+
+    if (e.data.source === 'impeccable-ready') {
+      injected = true;
+      if (pendingScan) {
+        pendingScan = false;
+        sendScanCommand();
+      }
+    }
+  });
+
+  // Forward "page is active" signal to the extension when the cursor moves over the page.
+  // This is the reliable way to know the user has left the DevTools panel — the panel's
+  // own pointerleave/mouseleave events are unreliable on fast cursor movement.
+  let lastPageActive = 0;
+  document.addEventListener('pointermove', () => {
+    const now = Date.now();
+    if (now - lastPageActive < 150) return; // throttle
+    lastPageActive = now;
+    chrome.runtime.sendMessage({ action: 'page-pointer-active' }).catch(() => {});
+  }, { passive: true, capture: true });
+
+  // SPA navigation detection (pushState/replaceState don't fire events, but
+  // popstate and hashchange cover back/forward and hash navigation)
+  let lastUrl = location.href;
+  function onPossibleNavigation() {
+    if (location.href === lastUrl) return;
+    lastUrl = location.href;
+    if (injected) {
+      // Detector is still loaded in page context, just re-scan after DOM settles
+      setTimeout(sendScanCommand, 500);
+    }
+  }
+  window.addEventListener('popstate', onPossibleNavigation);
+  window.addEventListener('hashchange', onPossibleNavigation);
+
+  function sendScanCommand() {
+    const msg = { source: 'impeccable-command', action: 'scan' };
+    if (scanConfig) msg.config = scanConfig;
+    window.postMessage(msg, '*');
+  }
+
+  function injectAndScan() {
+    if (injected) {
+      sendScanCommand();
+      return;
+    }
+
+    // Set the extension flag via a data attribute (CSP-safe: content scripts share the DOM)
+    document.documentElement.dataset.impeccableExtension = 'true';
+
+    // Inject the detector script into page context
+    const script = document.createElement('script');
+    script.src = chrome.runtime.getURL('detector/detect.js');
+    script.dataset.impeccableExtension = 'true';
+    pendingScan = true;
+    script.onload = () => script.remove();
+    script.onerror = () => {
+      script.remove();
+      // Fallback: use chrome.scripting.executeScript for strict CSP pages
+      chrome.runtime.sendMessage({ action: 'inject-fallback' });
+    };
+    (document.head || document.documentElement).appendChild(script);
+  }
+})();

extension/devtools/devtools.js 🔗

@@ -0,0 +1,50 @@
+/**
+ * Impeccable DevTools Extension - DevTools Page
+ *
+ * Creates the Impeccable panel and triggers an auto-scan when DevTools opens.
+ * This page lives for the entire DevTools session -- its port disconnect
+ * is the canonical signal that DevTools has closed.
+ */
+
+chrome.devtools.panels.create(
+  'Impeccable',
+  'icons/icon-32.png',
+  'devtools/panel.html'
+);
+
+// Sidebar pane in the Elements panel: shows findings for the currently selected element
+chrome.devtools.panels.elements.createSidebarPane('Impeccable', (sidebar) => {
+  sidebar.setPage('devtools/sidebar.html');
+  sidebar.setHeight('200px');
+});
+
+// Lifecycle port to the service worker. Auto-reconnects if the SW gets terminated
+// (which can happen in MV3 after ~30s of inactivity, especially when the browser is unfocused).
+const portName = `impeccable-devtools-${chrome.devtools.inspectedWindow.tabId}`;
+let lifecyclePort = null;
+let firstConnect = true;
+function connectLifecycle() {
+  lifecyclePort = chrome.runtime.connect({ name: portName });
+  // On the very first connection, decide whether to auto-scan based on the user's setting.
+  // Default ('panel'): wait until the user opens the Impeccable panel or sidebar.
+  // Opt-in ('devtools'): scan immediately when DevTools opens.
+  if (firstConnect) {
+    firstConnect = false;
+    chrome.storage.sync.get({ autoScan: 'panel' }, (settings) => {
+      if (settings.autoScan === 'devtools') {
+        try { lifecyclePort?.postMessage({ action: 'scan' }); } catch {}
+      }
+    });
+  }
+  lifecyclePort.onDisconnect.addListener(() => {
+    lifecyclePort = null;
+    // Reconnect on the next tick so the SW sees a fresh connection
+    setTimeout(connectLifecycle, 100);
+  });
+}
+connectLifecycle();
+
+// Heartbeat to keep the SW alive
+setInterval(() => {
+  try { lifecyclePort?.postMessage({ action: 'ping' }); } catch {}
+}, 20000);

extension/devtools/panel.css 🔗

@@ -0,0 +1,520 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+/* Light theme (default DevTools) */
+:root {
+  --bg: #fff;
+  --bg-subtle: #f5f5f5;
+  --bg-hover: #eee;
+  --text: #1a1a1a;
+  --text-dim: #666;
+  --accent: oklch(48% 0.25 350);
+  --accent-dim: oklch(40% 0.18 350);
+  --border: #ddd;
+  --radius: 6px;
+}
+
+/* Dark theme (set via JS from chrome.devtools.panels.themeName) */
+.theme-dark {
+  --bg: #1a1a1a;
+  --bg-subtle: #242424;
+  --bg-hover: #2a2a2a;
+  --text: #f5f3ef;
+  --text-dim: #999;
+  --accent: oklch(55% 0.25 350);
+  --accent-dim: oklch(45% 0.18 350);
+  --border: #333;
+}
+
+body {
+  background: var(--bg);
+  color: var(--text);
+  font-family: system-ui, -apple-system, sans-serif;
+  font-size: 12px;
+  line-height: 1.5;
+  overflow-y: auto;
+  height: 100vh;
+}
+
+/* Toolbar */
+.toolbar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 8px 12px;
+  border-bottom: 1px solid var(--border);
+  background: var(--bg);
+  position: sticky;
+  top: 0;
+  z-index: 10;
+}
+
+.toolbar-left {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.toolbar-right {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.logo {
+  font-size: 18px;
+  font-weight: 500;
+  color: var(--text);
+  opacity: 0.7;
+}
+
+h1 {
+  font-size: 13px;
+  font-weight: 600;
+  letter-spacing: 0.01em;
+}
+
+.badge {
+  background: var(--accent);
+  color: white;
+  font-size: 11px;
+  font-weight: 600;
+  padding: 1px 6px;
+  border-radius: 10px;
+  min-width: 20px;
+  text-align: center;
+  display: none;
+}
+
+.badge.visible {
+  display: inline-block;
+}
+
+/* Tool buttons */
+.tool-btn {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 28px;
+  height: 28px;
+  border: none;
+  border-radius: var(--radius);
+  background: transparent;
+  color: var(--text-dim);
+  cursor: pointer;
+  transition: background 0.15s, color 0.15s;
+}
+
+.tool-btn:hover {
+  background: var(--bg-hover);
+  color: var(--text);
+}
+
+.tool-btn.inactive {
+  opacity: 0.4;
+}
+
+/* Findings */
+#findings-container {
+  padding: 8px;
+}
+
+.finding-group {
+  margin-bottom: 2px;
+}
+
+.group-header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 6px 8px;
+  border-radius: var(--radius);
+  cursor: pointer;
+  user-select: none;
+  transition: background 0.15s;
+}
+
+.group-header:hover {
+  background: var(--bg-hover);
+}
+
+.group-chevron {
+  font-size: 10px;
+  color: var(--text-dim);
+  transition: transform 0.15s;
+  width: 12px;
+  flex-shrink: 0;
+}
+
+.group-header.collapsed .group-chevron {
+  transform: rotate(-90deg);
+}
+
+.group-name {
+  font-weight: 600;
+  font-size: 12px;
+  flex: 1;
+  min-width: 0;
+}
+
+.group-count {
+  font-size: 11px;
+  color: var(--text-dim);
+  font-weight: 500;
+}
+
+.group-items {
+  overflow: hidden;
+}
+
+.group-header.collapsed + .group-items {
+  display: none;
+}
+
+.finding-item {
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+  padding: 5px 8px 5px 28px;
+  border-radius: var(--radius);
+  cursor: pointer;
+  transition: background 0.15s;
+}
+
+.finding-item:hover {
+  background: var(--bg-hover);
+}
+
+.finding-row {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  min-width: 0;
+}
+
+.finding-row .finding-selector {
+  flex: 1;
+  min-width: 0;
+}
+
+.finding-copy {
+  display: none;
+  align-items: center;
+  justify-content: center;
+  width: 18px;
+  height: 18px;
+  flex-shrink: 0;
+  border: none;
+  border-radius: 4px;
+  background: transparent;
+  color: var(--text-dim);
+  cursor: pointer;
+  transition: background 0.15s, color 0.15s;
+}
+
+.finding-item:hover .finding-copy {
+  display: flex;
+}
+
+.finding-copy:hover {
+  background: var(--bg);
+  color: var(--accent);
+}
+
+.finding-copy.copied {
+  color: var(--accent);
+}
+
+.tool-btn.copied {
+  color: var(--accent);
+}
+
+.finding-selector {
+  font-family: ui-monospace, 'SF Mono', 'Cascadia Code', monospace;
+  font-size: 11px;
+  color: var(--accent);
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.finding-detail {
+  font-size: 11px;
+  color: var(--text-dim);
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.finding-description {
+  font-size: 11px;
+  color: var(--text-dim);
+  opacity: 0.7;
+  line-height: 1.4;
+  padding: 2px 0 4px;
+  display: none;
+}
+
+.finding-item:hover .finding-description {
+  display: block;
+}
+
+/* Finding tags (page-level, hidden, etc.) */
+.finding-tag {
+  display: inline-block;
+  font-size: 9px;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  padding: 1px 5px;
+  border-radius: 3px;
+  margin-bottom: 2px;
+}
+
+.tag-page {
+  color: var(--accent-dim);
+  background: transparent;
+}
+
+.tag-hidden {
+  color: var(--text-dim);
+  background: var(--bg-hover);
+}
+
+.finding-item.is-hidden {
+  opacity: 0.55;
+  cursor: default;
+}
+
+.finding-item.is-hidden:hover {
+  background: transparent;
+}
+
+/* Empty state */
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 48px 24px;
+  text-align: center;
+}
+
+.empty-icon {
+  font-size: 32px;
+  font-weight: 500;
+  opacity: 0.3;
+  margin-bottom: 12px;
+}
+
+.empty-title {
+  font-size: 13px;
+  font-weight: 500;
+  margin-bottom: 4px;
+}
+
+.empty-hint {
+  font-size: 12px;
+  color: var(--text-dim);
+}
+
+/* Category sections */
+.category-section {
+  margin-bottom: 4px;
+}
+
+.category-header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 8px 8px 4px;
+}
+
+.category-dot {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  flex-shrink: 0;
+}
+
+.category-dot-slop {
+  background: oklch(55% 0.25 350);
+}
+
+.category-dot-quality {
+  background: var(--text-dim);
+}
+
+.category-name {
+  font-size: 11px;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  color: var(--text-dim);
+}
+
+.category-count {
+  font-size: 11px;
+  color: var(--text-dim);
+  font-weight: 500;
+}
+
+/* Settings */
+#settings-container {
+  border-bottom: 1px solid var(--border);
+  padding: 0 8px 8px;
+}
+
+.settings-header {
+  font-size: 11px;
+  font-weight: 600;
+  color: var(--text-dim);
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  padding: 8px 8px 6px;
+}
+
+.settings-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 1px 12px;
+  padding-bottom: 8px;
+}
+
+#settings-list .settings-header {
+  padding: 8px 8px 4px;
+}
+
+.setting-row {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 4px 8px 8px;
+  gap: 12px;
+}
+
+.setting-label {
+  font-size: 11px;
+  color: var(--text);
+}
+
+.setting-segmented {
+  display: inline-flex;
+  border: 1px solid var(--border);
+  border-radius: var(--radius);
+  overflow: hidden;
+}
+
+.setting-segmented button {
+  background: transparent;
+  border: none;
+  color: var(--text-dim);
+  font-size: 11px;
+  padding: 3px 8px;
+  cursor: pointer;
+  font-family: inherit;
+  border-right: 1px solid var(--border);
+}
+
+.setting-segmented button:last-child {
+  border-right: none;
+}
+
+.setting-segmented button:hover {
+  color: var(--text);
+}
+
+.setting-segmented button.active {
+  background: var(--accent);
+  color: white;
+}
+
+.setting-switch {
+  position: relative;
+  display: inline-block;
+  width: 28px;
+  height: 16px;
+  cursor: pointer;
+  flex-shrink: 0;
+}
+
+.setting-switch input {
+  position: absolute;
+  opacity: 0;
+  width: 0;
+  height: 0;
+}
+
+.setting-switch-track {
+  position: absolute;
+  top: 0; left: 0; right: 0; bottom: 0;
+  background: var(--border);
+  border-radius: 8px;
+  transition: background 0.15s ease;
+}
+
+.setting-switch-track::before {
+  content: '';
+  position: absolute;
+  top: 2px;
+  left: 2px;
+  width: 12px;
+  height: 12px;
+  background: var(--bg);
+  border-radius: 50%;
+  transition: transform 0.15s ease;
+}
+
+.setting-switch input:checked + .setting-switch-track {
+  background: var(--accent);
+}
+
+.setting-switch input:checked + .setting-switch-track::before {
+  transform: translateX(12px);
+}
+
+.setting-rule {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  padding: 3px 8px;
+  border-radius: var(--radius);
+  font-size: 11px;
+  cursor: pointer;
+  transition: background 0.15s;
+}
+
+.setting-rule:hover {
+  background: var(--bg-hover);
+}
+
+.setting-rule input[type="checkbox"] {
+  margin: 0;
+  accent-color: var(--accent);
+}
+
+/* Scanning state */
+.scanning-indicator {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 12px;
+  color: var(--text-dim);
+  font-size: 12px;
+}
+
+.scanning-dot {
+  width: 6px;
+  height: 6px;
+  border-radius: 50%;
+  background: var(--accent);
+  animation: pulse 1s ease-in-out infinite;
+}
+
+@keyframes pulse {
+  0%, 100% { opacity: 0.3; }
+  50% { opacity: 1; }
+}

extension/devtools/panel.html 🔗

@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <link rel="stylesheet" href="panel.css">
+</head>
+<body>
+  <header class="toolbar">
+    <div class="toolbar-left">
+      <span class="logo">/</span>
+      <h1>Impeccable</h1>
+      <span class="badge" id="badge">0</span>
+    </div>
+    <div class="toolbar-right">
+      <button class="tool-btn" id="btn-copy-all" title="Copy all findings">
+        <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M11 1H3a2 2 0 0 0-2 2v10h2V3h8V1zm3 3H7a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 11H7V6h7v9z" fill="currentColor"/></svg>
+      </button>
+      <button class="tool-btn" id="btn-rescan" title="Re-scan page">
+        <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M13.65 2.35A8 8 0 1 0 16 8h-2a6 6 0 1 1-1.76-4.24L10 6h6V0l-2.35 2.35z" fill="currentColor"/></svg>
+      </button>
+      <button class="tool-btn" id="btn-toggle" title="Toggle overlays">
+        <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M8 3C4.36 3 1.26 5.28 0 8.5c1.26 3.22 4.36 5.5 8 5.5s6.74-2.28 8-5.5C14.74 5.28 11.64 3 8 3zm0 9.17c-2.58 0-4.67-2.09-4.67-4.67S5.42 2.83 8 2.83s4.67 2.09 4.67 4.67S10.58 12.17 8 12.17zM8 5a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7z" fill="currentColor"/></svg>
+      </button>
+      <button class="tool-btn" id="btn-settings" title="Settings">
+        <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M13.6 8.8c.04-.26.06-.53.06-.8s-.02-.54-.06-.8l1.74-1.36a.42.42 0 0 0 .1-.52l-1.64-2.84a.41.41 0 0 0-.5-.18l-2.06.82a5.96 5.96 0 0 0-1.38-.8L9.5.2A.4.4 0 0 0 9.1 0H5.82a.4.4 0 0 0-.4.34l-.3 2.12c-.5.2-.96.48-1.38.8l-2.06-.82a.4.4 0 0 0-.5.18L-.46 5.46a.41.41 0 0 0 .1.52L1.38 7.34c-.04.26-.06.53-.06.8s.02.54.06.8L-.36 10.3a.42.42 0 0 0-.1.52l1.64 2.84c.1.18.32.24.5.18l2.06-.82c.42.32.88.6 1.38.8l.3 2.12a.4.4 0 0 0 .4.34h3.28a.4.4 0 0 0 .4-.34l.3-2.12c.5-.2.96-.48 1.38-.8l2.06.82c.18.08.4 0 .5-.18l1.64-2.84a.41.41 0 0 0-.1-.52L13.6 8.8zM7.46 10.8c-1.56 0-2.82-1.26-2.82-2.8s1.26-2.8 2.82-2.8 2.82 1.26 2.82 2.8-1.26 2.8-2.82 2.8z" fill="currentColor"/></svg>
+      </button>
+    </div>
+  </header>
+
+  <div id="settings-container" style="display: none">
+    <div class="settings-header">Preferences</div>
+    <div class="setting-row">
+      <span class="setting-label">Auto-scan</span>
+      <div class="setting-segmented" id="auto-scan-mode">
+        <button data-value="panel">When panel opens</button>
+        <button data-value="devtools">When DevTools opens</button>
+      </div>
+    </div>
+    <div class="setting-row">
+      <span class="setting-label">Line length</span>
+      <div class="setting-segmented" id="line-length-mode">
+        <button data-value="strict">Strict (80)</button>
+        <button data-value="lax">Lax (120)</button>
+      </div>
+    </div>
+    <div class="setting-row">
+      <span class="setting-label">Highlight blur</span>
+      <label class="setting-switch">
+        <input type="checkbox" id="spotlight-blur-toggle">
+        <span class="setting-switch-track"></span>
+      </label>
+    </div>
+    <div id="settings-list"></div>
+  </div>
+
+  <main id="findings-container">
+    <div class="empty-state" id="empty-state">
+      <div class="empty-icon">/</div>
+      <p class="empty-title">No anti-patterns detected</p>
+      <p class="empty-hint">Overlays will appear on the page when issues are found</p>
+    </div>
+  </main>
+
+  <script src="panel.js"></script>
+</body>
+</html>

extension/devtools/panel.js 🔗

@@ -0,0 +1,519 @@
+/**
+ * Impeccable DevTools Extension - Panel
+ *
+ * Displays findings, provides controls for scanning and overlay toggling,
+ * and allows clicking findings to inspect elements.
+ */
+
+// Match the DevTools theme (light or dark)
+if (chrome.devtools.panels.themeName === 'dark') {
+  document.documentElement.classList.add('theme-dark');
+}
+
+const tabId = chrome.devtools.inspectedWindow.tabId;
+
+// Auto-reconnecting port. Service workers in MV3 can be terminated after ~30s of
+// inactivity (especially when the browser window is unfocused). When they restart,
+// the existing port becomes invalid. We recreate it lazily on the next use.
+let port = null;
+function getPort() {
+  if (port) return port;
+  port = chrome.runtime.connect({ name: `impeccable-panel-${tabId}` });
+  port.onMessage.addListener(handlePortMessage);
+  port.onDisconnect.addListener(() => { port = null; });
+  return port;
+}
+function postToPort(msg) {
+  try {
+    getPort().postMessage(msg);
+  } catch {
+    // Port died mid-call. Drop it and try once more with a fresh port.
+    port = null;
+    try { getPort().postMessage(msg); } catch { /* give up silently */ }
+  }
+}
+
+const badge = document.getElementById('badge');
+const container = document.getElementById('findings-container');
+const emptyState = document.getElementById('empty-state');
+const btnRescan = document.getElementById('btn-rescan');
+const btnToggle = document.getElementById('btn-toggle');
+const btnCopyAll = document.getElementById('btn-copy-all');
+const settingsContainer = document.getElementById('settings-container');
+const settingsList = document.getElementById('settings-list');
+const btnSettings = document.getElementById('btn-settings');
+
+let overlaysVisible = true;
+let allAntipatterns = [];
+let disabledRules = [];
+let currentFindings = [];
+
+// Load antipatterns list and disabled rules
+async function initSettings() {
+  try {
+    const resp = await fetch(chrome.runtime.getURL('detector/antipatterns.json'));
+    allAntipatterns = await resp.json();
+  } catch { allAntipatterns = []; }
+
+  const stored = await chrome.storage.sync.get({
+    disabledRules: [],
+    lineLengthMode: 'strict',
+    spotlightBlur: true,
+    autoScan: 'panel',
+  });
+  disabledRules = stored.disabledRules;
+  renderSettings();
+  initLineLengthControl(stored.lineLengthMode);
+  initSpotlightBlurToggle(stored.spotlightBlur);
+  initAutoScanControl(stored.autoScan);
+}
+
+function initAutoScanControl(currentMode) {
+  const group = document.getElementById('auto-scan-mode');
+  if (!group) return;
+  for (const btn of group.querySelectorAll('button')) {
+    btn.classList.toggle('active', btn.dataset.value === currentMode);
+    btn.addEventListener('click', async () => {
+      const mode = btn.dataset.value;
+      for (const b of group.querySelectorAll('button')) {
+        b.classList.toggle('active', b === btn);
+      }
+      await chrome.storage.sync.set({ autoScan: mode });
+    });
+  }
+}
+
+function initLineLengthControl(currentMode) {
+  const group = document.getElementById('line-length-mode');
+  if (!group) return;
+  for (const btn of group.querySelectorAll('button')) {
+    btn.classList.toggle('active', btn.dataset.value === currentMode);
+    btn.addEventListener('click', async () => {
+      const mode = btn.dataset.value;
+      for (const b of group.querySelectorAll('button')) {
+        b.classList.toggle('active', b === btn);
+      }
+      await chrome.storage.sync.set({ lineLengthMode: mode });
+      chrome.runtime.sendMessage({ action: 'disabled-rules-changed' });
+    });
+  }
+}
+
+function initSpotlightBlurToggle(currentValue) {
+  const cb = document.getElementById('spotlight-blur-toggle');
+  if (!cb) return;
+  cb.checked = currentValue;
+  cb.addEventListener('change', async () => {
+    await chrome.storage.sync.set({ spotlightBlur: cb.checked });
+    chrome.runtime.sendMessage({ action: 'disabled-rules-changed' });
+  });
+}
+
+function renderSettings() {
+  settingsList.innerHTML = '';
+
+  const categories = {
+    slop: { label: 'AI tells', items: [] },
+    quality: { label: 'Quality', items: [] },
+  };
+  for (const ap of allAntipatterns) {
+    const cat = ap.category || 'quality';
+    (categories[cat] || categories.quality).items.push(ap);
+  }
+
+  for (const [, group] of Object.entries(categories)) {
+    if (!group.items.length) continue;
+
+    const header = document.createElement('div');
+    header.className = 'settings-header';
+    header.textContent = group.label;
+    settingsList.appendChild(header);
+
+    const grid = document.createElement('div');
+    grid.className = 'settings-grid';
+
+    for (const ap of group.items) {
+      const label = document.createElement('label');
+      label.className = 'setting-rule';
+
+      const checkbox = document.createElement('input');
+      checkbox.type = 'checkbox';
+      checkbox.checked = !disabledRules.includes(ap.id);
+      checkbox.addEventListener('change', () => toggleRule(ap.id, checkbox.checked));
+
+      const text = document.createElement('span');
+      text.textContent = ap.name;
+
+      label.appendChild(checkbox);
+      label.appendChild(text);
+      grid.appendChild(label);
+    }
+    settingsList.appendChild(grid);
+  }
+}
+
+async function toggleRule(ruleId, enabled) {
+  if (enabled) {
+    disabledRules = disabledRules.filter(id => id !== ruleId);
+  } else {
+    if (!disabledRules.includes(ruleId)) disabledRules.push(ruleId);
+  }
+  await chrome.storage.sync.set({ disabledRules });
+  chrome.runtime.sendMessage({ action: 'disabled-rules-changed' });
+}
+
+// Listen for messages from the service worker (called by getPort() on each new connection)
+function handlePortMessage(msg) {
+  if (msg.action === 'page-pointer-active') {
+    // Cursor is active on the page → user has left the panel
+    setHoveredItem(null);
+    return;
+  }
+  if (msg.action === 'findings' || msg.action === 'state') {
+    renderFindings(msg.findings || []);
+    if (msg.overlaysVisible !== undefined) {
+      overlaysVisible = msg.overlaysVisible;
+      updateToggleButton();
+    }
+  }
+  if (msg.action === 'overlays-toggled') {
+    overlaysVisible = msg.visible;
+    updateToggleButton();
+  }
+  if (msg.action === 'navigated') {
+    showScanning();
+  }
+}
+
+// Initial connection
+getPort();
+
+// Heartbeat to keep the MV3 service worker alive while the panel is open.
+// SWs can be terminated after ~30s of inactivity, especially when the browser is unfocused.
+setInterval(() => postToPort({ action: 'ping' }), 20000);
+
+// Controls
+btnRescan.addEventListener('click', () => {
+  showScanning();
+  postToPort({ action: 'scan' });
+});
+
+btnToggle.addEventListener('click', () => {
+  postToPort({ action: 'toggle-overlays' });
+});
+
+btnSettings.addEventListener('click', () => {
+  const isVisible = settingsContainer.style.display !== 'none';
+  settingsContainer.style.display = isVisible ? 'none' : '';
+  btnSettings.classList.toggle('active', !isVisible);
+});
+
+function updateToggleButton() {
+  btnToggle.title = overlaysVisible ? 'Hide overlays' : 'Show overlays';
+  btnToggle.classList.toggle('inactive', !overlaysVisible);
+}
+
+function showScanning() {
+  container.innerHTML = `
+    <div class="scanning-indicator">
+      <div class="scanning-dot"></div>
+      Scanning page...
+    </div>`;
+}
+
+// Maps each anti-pattern to the most relevant Impeccable skill(s) for fixing it.
+// These are suggestions; the user decides whether and how to apply them.
+const FIX_SKILLS = {
+  // AI slop
+  'side-tab':                'distill, polish',
+  'border-accent-on-rounded':'distill, polish',
+  'overused-font':           'typeset',
+  'single-font':             'typeset',
+  'flat-type-hierarchy':     'typeset',
+  'gradient-text':           'typeset, distill',
+  'ai-color-palette':        'colorize, distill',
+  'nested-cards':            'distill, arrange',
+  'monotonous-spacing':      'arrange',
+  'everything-centered':     'arrange',
+  'bounce-easing':           'animate',
+  'dark-glow':               'quieter, distill',
+  'icon-tile-stacked-above-heading': 'distill, arrange',
+  // Quality
+  'pure-black-white':        'colorize',
+  'gray-on-color':           'colorize',
+  'low-contrast':            'colorize, audit',
+  'layout-transition':       'animate, optimize',
+  'line-length':             'arrange, typeset',
+  'cramped-padding':         'arrange, polish',
+  'tight-leading':           'typeset',
+  'skipped-heading':         'audit, harden',
+  'justified-text':          'typeset',
+  'tiny-text':               'typeset',
+  'all-caps-body':           'typeset',
+  'wide-tracking':           'typeset',
+};
+
+function fixSkillFor(type) {
+  const skills = FIX_SKILLS[type] || 'polish';
+  // Prefix each comma-separated skill with a slash for clarity
+  return skills.split(',').map(s => '/' + s.trim()).join(', ');
+}
+
+// Returns a sorted array of unique skills referenced by the given findings,
+// most-frequent first. Each entry already has the leading slash.
+function uniqueSkillsForFindings(findings) {
+  const counts = new Map();
+  for (const item of findings) {
+    for (const f of item.findings) {
+      const list = (FIX_SKILLS[f.type] || 'polish').split(',').map(s => '/' + s.trim());
+      for (const s of list) {
+        counts.set(s, (counts.get(s) || 0) + 1);
+      }
+    }
+  }
+  return [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([s]) => s);
+}
+
+function getInspectedUrl() {
+  return new Promise((resolve) => {
+    // Strip the URL fragment — anchors are noise for "what page is this from"
+    chrome.devtools.inspectedWindow.eval(
+      '(function(){var u=new URL(location.href);u.hash="";return u.toString();})()',
+      (result) => resolve(typeof result === 'string' ? result : '')
+    );
+  });
+}
+
+async function formatFindingsForCopy(findings) {
+  if (!findings.length) return 'Impeccable found no anti-patterns on this page.';
+  const url = await getInspectedUrl();
+  const lines = ['# Impeccable findings'];
+  if (url) lines.push(`URL: ${url}`);
+  lines.push('');
+
+  const groups = { slop: [], quality: [] };
+  for (const item of findings) {
+    for (const f of item.findings) {
+      const cat = f.category || 'quality';
+      groups[cat].push({ ...f, selector: item.selector, isPageLevel: item.isPageLevel });
+    }
+  }
+
+  if (groups.slop.length) {
+    lines.push(`## AI tells (${groups.slop.length})`);
+    for (const f of groups.slop) {
+      const where = f.isPageLevel ? '_(page-level)_' : `\`${f.selector}\``;
+      lines.push(`- **${f.name}** at ${where}: ${f.detail}`);
+    }
+    lines.push('');
+  }
+
+  if (groups.quality.length) {
+    lines.push(`## Quality issues (${groups.quality.length})`);
+    for (const f of groups.quality) {
+      const where = f.isPageLevel ? '_(page-level)_' : `\`${f.selector}\``;
+      lines.push(`- **${f.name}** at ${where}: ${f.detail}`);
+    }
+    lines.push('');
+  }
+
+  // Roll up suggested skills across all findings (most-relevant first)
+  const skills = uniqueSkillsForFindings(findings);
+  if (skills.length) {
+    lines.push(`Suggested Impeccable skills to fix: ${skills.join(', ')}`);
+    lines.push('');
+  }
+
+  lines.push('---');
+  lines.push('Detected by [Impeccable](https://impeccable.style). Skills are suggestions, not required.');
+  return lines.join('\n');
+}
+
+async function formatSingleFindingForCopy(item, finding) {
+  const url = await getInspectedUrl();
+  const where = item.isPageLevel ? '_(page-level)_' : `\`${item.selector}\``;
+  const lines = [`# Impeccable: ${finding.name}`];
+  if (url) lines.push(`URL: ${url}`);
+  lines.push(`Element: ${where}`);
+  lines.push(`Detail: ${finding.detail}`);
+  lines.push('');
+  lines.push(finding.description);
+  lines.push('');
+  lines.push(`Suggested Impeccable skill(s) to fix: ${fixSkillFor(finding.type)}`);
+  return lines.join('\n');
+}
+
+async function copyToClipboard(text, btn) {
+  if (text instanceof Promise) text = await text;
+  try {
+    await navigator.clipboard.writeText(text);
+    if (btn) {
+      const orig = btn.title;
+      btn.title = 'Copied!';
+      btn.classList.add('copied');
+      setTimeout(() => {
+        btn.title = orig;
+        btn.classList.remove('copied');
+      }, 1200);
+    }
+  } catch (err) {
+    console.warn('Copy failed', err);
+  }
+}
+
+btnCopyAll.addEventListener('click', () => {
+  copyToClipboard(formatFindingsForCopy(currentFindings), btnCopyAll);
+});
+
+// Delegated hover tracking on the findings container.
+// Reliably handles cursor moving between items, into children, or out of the panel.
+let currentHoverSelector = null;
+function setHoveredItem(selector) {
+  if (selector === currentHoverSelector) return;
+  currentHoverSelector = selector;
+  if (selector) {
+    postToPort({ action: 'highlight', selector });
+  } else {
+    postToPort({ action: 'unhighlight' });
+  }
+}
+
+container.addEventListener('pointermove', (e) => {
+  const item = e.target.closest('.finding-item');
+  const selector = item && !item.classList.contains('is-hidden') ? item.dataset.selector || null : null;
+  setHoveredItem(selector);
+});
+
+// Slow-cursor fallbacks (these fire reliably for slow movements)
+container.addEventListener('pointerleave', () => setHoveredItem(null));
+window.addEventListener('blur', () => setHoveredItem(null));
+
+
+function renderFindings(findings) {
+  currentFindings = findings;
+  if (!findings.length) {
+    container.innerHTML = '';
+    container.appendChild(emptyState);
+    emptyState.style.display = '';
+    badge.classList.remove('visible');
+    badge.textContent = '0';
+    return;
+  }
+
+  emptyState.style.display = 'none';
+
+  // Count total element-level findings
+  const totalCount = findings.reduce((sum, f) => sum + f.findings.length, 0);
+  badge.textContent = String(totalCount);
+  badge.classList.add('visible');
+
+  // Group findings by category, then by anti-pattern type
+  const categories = { slop: new Map(), quality: new Map() };
+  for (const item of findings) {
+    for (const f of item.findings) {
+      const cat = f.category || 'quality';
+      const groups = categories[cat] || categories.quality;
+      if (!groups.has(f.type)) {
+        groups.set(f.type, { name: f.name, description: f.description, items: [] });
+      }
+      groups.get(f.type).items.push({
+        selector: item.selector,
+        tagName: item.tagName,
+        isPageLevel: item.isPageLevel,
+        isHidden: item.isHidden,
+        detail: f.detail,
+      });
+    }
+  }
+
+  container.innerHTML = '';
+
+  const CATEGORY_LABELS = { slop: 'AI tells', quality: 'Quality issues' };
+  for (const [catKey, groups] of Object.entries(categories)) {
+    if (groups.size === 0) continue;
+
+    const catCount = [...groups.values()].reduce((sum, g) => sum + g.items.length, 0);
+    const section = document.createElement('div');
+    section.className = 'category-section category-' + catKey;
+
+    const catHeader = document.createElement('div');
+    catHeader.className = 'category-header';
+    catHeader.innerHTML = `
+      <span class="category-dot category-dot-${catKey}"></span>
+      <span class="category-name">${CATEGORY_LABELS[catKey]}</span>
+      <span class="category-count">${catCount}</span>`;
+    section.appendChild(catHeader);
+
+    for (const [type, group] of groups) {
+    const groupEl = document.createElement('div');
+    groupEl.className = 'finding-group';
+
+    const header = document.createElement('div');
+    header.className = 'group-header';
+    header.innerHTML = `
+      <span class="group-chevron">&#9660;</span>
+      <span class="group-name">${escapeHtml(group.name)}</span>
+      <span class="group-count">${group.items.length}</span>`;
+    header.addEventListener('click', () => header.classList.toggle('collapsed'));
+    groupEl.appendChild(header);
+
+    const itemsEl = document.createElement('div');
+    itemsEl.className = 'group-items';
+
+    for (const item of group.items) {
+      const itemEl = document.createElement('div');
+      itemEl.className = 'finding-item' + (item.isHidden ? ' is-hidden' : '');
+      const tag = item.isPageLevel
+        ? '<span class="finding-tag tag-page">page</span>'
+        : item.isHidden ? '<span class="finding-tag tag-hidden" title="Element is currently hidden on the page">hidden</span>' : '';
+      itemEl.innerHTML = `
+        ${tag}
+        <div class="finding-row">
+          <span class="finding-selector">${escapeHtml(item.selector)}</span>
+          <button class="finding-copy" title="Copy this finding">
+            <svg width="11" height="11" viewBox="0 0 16 16" fill="none"><path d="M11 1H3a2 2 0 0 0-2 2v10h2V3h8V1zm3 3H7a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 11H7V6h7v9z" fill="currentColor"/></svg>
+          </button>
+        </div>
+        <span class="finding-detail">${escapeHtml(item.detail)}</span>
+        <span class="finding-description">${escapeHtml(group.description)}</span>`;
+
+      const copyBtn = itemEl.querySelector('.finding-copy');
+      const finding = { type, name: group.name, description: group.description, detail: item.detail };
+      copyBtn.addEventListener('click', (e) => {
+        e.stopPropagation();
+        copyToClipboard(formatSingleFindingForCopy(item, finding), copyBtn);
+      });
+
+      if (!item.isPageLevel && !item.isHidden) {
+        itemEl.dataset.selector = item.selector;
+        itemEl.addEventListener('click', () => inspectElement(item.selector));
+      }
+
+      itemsEl.appendChild(itemEl);
+    }
+
+    groupEl.appendChild(itemsEl);
+    section.appendChild(groupEl);
+  }
+
+    container.appendChild(section);
+  }
+}
+
+function inspectElement(selector) {
+  const escaped = selector.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
+  chrome.devtools.inspectedWindow.eval(
+    `(function() {
+      var el = document.querySelector('${escaped}');
+      if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'center' }); inspect(el); }
+    })()`
+  );
+}
+
+function escapeHtml(str) {
+  const div = document.createElement('div');
+  div.textContent = str;
+  return div.innerHTML;
+}
+
+initSettings();

extension/devtools/sidebar.css 🔗

@@ -0,0 +1,97 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+:root {
+  --bg: #fff;
+  --text: #1a1a1a;
+  --text-dim: #666;
+  --text-faint: #999;
+  --accent: oklch(48% 0.25 350);
+  --rule: #e8e6e2;
+}
+
+.theme-dark {
+  --bg: #1a1a1a;
+  --text: #f5f3ef;
+  --text-dim: #9a9590;
+  --text-faint: #666;
+  --accent: oklch(60% 0.25 350);
+  --rule: #2a2a2a;
+}
+
+body {
+  background: var(--bg);
+  color: var(--text);
+  font-family: -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
+  font-size: 12px;
+  line-height: 1.5;
+  padding: 12px 14px;
+}
+
+/* Empty / no-findings states */
+
+.state {
+  color: var(--text-faint);
+  font-size: 12px;
+  font-style: italic;
+  padding: 4px 0;
+}
+
+.state strong {
+  color: var(--text-dim);
+  font-style: normal;
+  font-weight: 600;
+}
+
+/* Finding list */
+
+.finding + .finding {
+  margin-top: 14px;
+  padding-top: 14px;
+  border-top: 1px solid var(--rule);
+}
+
+.finding-header {
+  display: flex;
+  align-items: baseline;
+  justify-content: space-between;
+  gap: 8px;
+  margin-bottom: 6px;
+}
+
+.finding-name {
+  font-weight: 600;
+  font-size: 12px;
+  color: var(--text);
+  letter-spacing: -0.005em;
+}
+
+.finding-name .marker {
+  color: var(--accent);
+  margin-right: 4px;
+}
+
+.finding-kind {
+  font-size: 9px;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--text-faint);
+  flex-shrink: 0;
+}
+
+.finding-detail {
+  font-family: ui-monospace, 'SF Mono', 'Cascadia Code', monospace;
+  font-size: 11px;
+  color: var(--text-dim);
+  margin-bottom: 6px;
+}
+
+.finding-description {
+  font-size: 11px;
+  color: var(--text-dim);
+  line-height: 1.55;
+}

extension/devtools/sidebar.html 🔗

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <link rel="stylesheet" href="sidebar.css">
+</head>
+<body>
+  <div id="sidebar-content">
+    <div class="state">Select an element to see Impeccable findings.</div>
+  </div>
+  <script src="sidebar.js"></script>
+</body>
+</html>

extension/devtools/sidebar.js 🔗

@@ -0,0 +1,103 @@
+/**
+ * Impeccable DevTools Extension - Elements Sidebar Pane
+ *
+ * Shows Impeccable findings for the currently selected element ($0) in the Elements panel.
+ */
+
+if (chrome.devtools.panels.themeName === 'dark') {
+  document.documentElement.classList.add('theme-dark');
+}
+
+const tabId = chrome.devtools.inspectedWindow.tabId;
+const content = document.getElementById('sidebar-content');
+let currentFindings = [];
+
+// Auto-reconnecting port (service worker may restart in MV3)
+let port = null;
+function getPort() {
+  if (port) return port;
+  port = chrome.runtime.connect({ name: `impeccable-sidebar-${tabId}` });
+  port.onMessage.addListener((msg) => {
+    if (msg.action === 'findings' || msg.action === 'state') {
+      currentFindings = msg.findings || [];
+      refreshForCurrentSelection();
+    }
+  });
+  port.onDisconnect.addListener(() => { port = null; });
+  return port;
+}
+getPort();
+
+chrome.devtools.panels.elements.onSelectionChanged.addListener(refreshForCurrentSelection);
+
+function refreshForCurrentSelection() {
+  if (!currentFindings.length) {
+    renderEmpty('No findings on this page yet.');
+    return;
+  }
+
+  // Collect non-page-level selectors and ask the inspected window which one matches $0
+  const selectors = [];
+  for (const item of currentFindings) {
+    if (item.isPageLevel || item.isHidden) continue;
+    selectors.push(item.selector);
+  }
+  if (!selectors.length) {
+    renderEmpty('No element-level findings on this page.');
+    return;
+  }
+
+  const code = `(function() {
+    var sels = ${JSON.stringify(selectors)};
+    var matched = [];
+    for (var i = 0; i < sels.length; i++) {
+      try { if (document.querySelector(sels[i]) === $0) matched.push(sels[i]); } catch (e) {}
+    }
+    return matched;
+  })()`;
+
+  chrome.devtools.inspectedWindow.eval(code, (matched) => {
+    if (!matched || !matched.length) {
+      renderNoFindings();
+      return;
+    }
+    const items = currentFindings.filter(item => matched.includes(item.selector));
+    render(items);
+  });
+}
+
+function renderEmpty(text) {
+  content.innerHTML = `<div class="state">${escapeHtml(text)}</div>`;
+}
+
+function renderNoFindings() {
+  content.innerHTML = `<div class="state"><strong>Clean.</strong> No anti-patterns on this element.</div>`;
+}
+
+function render(items) {
+  const html = [];
+  for (const item of items) {
+    for (const f of item.findings) {
+      const isSlop = f.category === 'slop';
+      const marker = isSlop ? '<span class="marker">\u2726</span>' : '';
+      const kind = isSlop ? 'AI tell' : 'Quality';
+      html.push(`
+        <div class="finding">
+          <div class="finding-header">
+            <span class="finding-name">${marker}${escapeHtml(f.name)}</span>
+            <span class="finding-kind">${kind}</span>
+          </div>
+          <div class="finding-detail">${escapeHtml(f.detail)}</div>
+          <div class="finding-description">${escapeHtml(f.description)}</div>
+        </div>
+      `);
+    }
+  }
+  content.innerHTML = html.join('');
+}
+
+function escapeHtml(str) {
+  const div = document.createElement('div');
+  div.textContent = str;
+  return div.innerHTML;
+}

extension/icons/icon.svg 🔗

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
+  <rect width="128" height="128" rx="24" fill="#1a1a1a"/>
+  <line x1="76" y1="24" x2="52" y2="104" stroke="#f5f3ef" stroke-width="7" stroke-linecap="round"/>
+</svg>

extension/manifest.json 🔗

@@ -0,0 +1,32 @@
+{
+  "manifest_version": 3,
+  "name": "Impeccable",
+  "description": "Detect common UI anti-patterns in any web page",
+  "version": "1.0.1",
+  "permissions": ["activeTab", "scripting", "storage", "webNavigation"],
+  "host_permissions": ["<all_urls>"],
+  "background": {
+    "service_worker": "background/service-worker.js"
+  },
+  "devtools_page": "devtools/devtools.html",
+  "action": {
+    "default_popup": "popup/popup.html",
+    "default_icon": {
+      "16": "icons/icon-16.png",
+      "32": "icons/icon-32.png",
+      "48": "icons/icon-48.png",
+      "128": "icons/icon-128.png"
+    }
+  },
+  "icons": {
+    "16": "icons/icon-16.png",
+    "48": "icons/icon-48.png",
+    "128": "icons/icon-128.png"
+  },
+  "web_accessible_resources": [
+    {
+      "resources": ["detector/detect.js"],
+      "matches": ["<all_urls>"]
+    }
+  ]
+}

extension/popup/popup.css 🔗

@@ -0,0 +1,110 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+body {
+  width: 220px;
+  background: #1a1a1a;
+  color: #f5f3ef;
+  font-family: system-ui, -apple-system, sans-serif;
+  font-size: 13px;
+  padding: 16px;
+}
+
+header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 16px;
+}
+
+.logo {
+  font-size: 18px;
+  font-weight: 500;
+  opacity: 0.7;
+}
+
+h1 {
+  font-size: 14px;
+  font-weight: 600;
+}
+
+.count-display {
+  text-align: center;
+  padding: 16px 0;
+  margin-bottom: 16px;
+}
+
+.count-number {
+  display: block;
+  font-size: 36px;
+  font-weight: 700;
+  line-height: 1;
+  margin-bottom: 4px;
+  color: #999;
+  transition: color 0.2s;
+}
+
+.count-number.has-findings {
+  color: oklch(55% 0.25 350);
+}
+
+.count-label {
+  font-size: 12px;
+  color: #999;
+}
+
+.actions {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  margin-bottom: 16px;
+}
+
+.btn {
+  display: block;
+  width: 100%;
+  padding: 8px 12px;
+  border: none;
+  border-radius: 6px;
+  font-size: 12px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: background 0.15s, opacity 0.15s;
+}
+
+.btn-primary {
+  background: oklch(55% 0.25 350);
+  color: white;
+}
+
+.btn-primary:hover {
+  background: oklch(50% 0.25 350);
+}
+
+.btn-secondary {
+  background: #333;
+  color: #ccc;
+}
+
+.btn-secondary:hover {
+  background: #3a3a3a;
+}
+
+footer {
+  text-align: center;
+  padding-top: 8px;
+  border-top: 1px solid #333;
+}
+
+footer a {
+  font-size: 11px;
+  color: #666;
+  text-decoration: none;
+}
+
+footer a:hover {
+  color: #999;
+}

extension/popup/popup.html 🔗

@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <link rel="stylesheet" href="popup.css">
+</head>
+<body>
+  <header>
+    <span class="logo">/</span>
+    <h1>Impeccable</h1>
+  </header>
+
+  <div class="count-display" id="count-display">
+    <span class="count-number" id="count-number">0</span>
+    <span class="count-label" id="count-label">anti-patterns</span>
+  </div>
+
+  <div class="actions">
+    <button class="btn btn-primary" id="btn-scan">Scan page</button>
+    <button class="btn btn-secondary" id="btn-toggle">Hide overlays</button>
+  </div>
+
+  <footer>
+    <a href="https://impeccable.style" id="link-site">impeccable.style</a>
+  </footer>
+
+  <script src="popup.js"></script>
+</body>
+</html>

extension/popup/popup.js 🔗

@@ -0,0 +1,67 @@
+/**
+ * Impeccable DevTools Extension - Popup
+ *
+ * Quick controls: scan, toggle overlays, and see finding count.
+ */
+
+const countNumber = document.getElementById('count-number');
+const countLabel = document.getElementById('count-label');
+const btnScan = document.getElementById('btn-scan');
+const btnToggle = document.getElementById('btn-toggle');
+
+let overlaysVisible = true;
+
+async function getActiveTabId() {
+  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
+  return tab?.id;
+}
+
+function updateFromState(state) {
+  if (!state) return;
+  const count = state.findings?.reduce((sum, f) => sum + f.findings.length, 0) || 0;
+  countNumber.textContent = String(count);
+  countNumber.classList.toggle('has-findings', count > 0);
+  countLabel.textContent = count === 1 ? 'anti-pattern' : 'anti-patterns';
+  overlaysVisible = state.overlaysVisible !== false;
+  btnToggle.textContent = overlaysVisible ? 'Hide overlays' : 'Show overlays';
+}
+
+async function loadState() {
+  const tabId = await getActiveTabId();
+  if (!tabId) return;
+  chrome.runtime.sendMessage({ action: 'get-state', tabId }, updateFromState);
+}
+
+// Listen for real-time updates from service worker
+chrome.runtime.onMessage.addListener((msg) => {
+  if (msg.action === 'findings-updated') {
+    const count = msg.findings?.reduce((sum, f) => sum + f.findings.length, 0) || 0;
+    countNumber.textContent = String(count);
+    countNumber.classList.toggle('has-findings', count > 0);
+    countLabel.textContent = count === 1 ? 'anti-pattern' : 'anti-patterns';
+    btnScan.textContent = 'Scan page';
+    btnScan.disabled = false;
+  }
+  if (msg.action === 'overlays-toggled-broadcast') {
+    overlaysVisible = msg.visible;
+    btnToggle.textContent = overlaysVisible ? 'Hide overlays' : 'Show overlays';
+  }
+});
+
+btnScan.addEventListener('click', async () => {
+  const tabId = await getActiveTabId();
+  if (!tabId) return;
+  btnScan.textContent = 'Scanning...';
+  btnScan.disabled = true;
+  chrome.runtime.sendMessage({ action: 'scan', tabId });
+});
+
+btnToggle.addEventListener('click', async () => {
+  const tabId = await getActiveTabId();
+  if (!tabId) return;
+  chrome.runtime.sendMessage({ action: 'toggle-overlays', tabId });
+  overlaysVisible = !overlaysVisible;
+  btnToggle.textContent = overlaysVisible ? 'Hide overlays' : 'Show overlays';
+});
+
+loadState();

functions/api/download/[type]/[provider]/[id].js 🔗

@@ -0,0 +1,46 @@
+import {
+	FILE_DOWNLOAD_PROVIDERS,
+	FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS
+} from "../../../../../lib/download-providers.js";
+
+const VALID_ID = /^[a-zA-Z0-9_-]+$/;
+
+export async function onRequestGet(context) {
+	const { type, provider, id } = context.params;
+
+	if (type !== 'skill' && type !== 'command') {
+		return Response.json({ error: "Invalid type" }, { status: 400 });
+	}
+
+	if (!provider || !FILE_DOWNLOAD_PROVIDERS.includes(provider)) {
+		return Response.json({ error: "Invalid provider" }, { status: 400 });
+	}
+
+	if (!id || !VALID_ID.test(id)) {
+		return Response.json({ error: "Invalid file ID" }, { status: 400 });
+	}
+
+	const configDir = FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS[provider];
+	if (!configDir) {
+		return Response.json({ error: "Invalid provider" }, { status: 400 });
+	}
+
+	const url = new URL(context.request.url);
+	url.pathname = `/_data/dist/${provider}/${configDir}/skills/${id}/SKILL.md`;
+
+	const response = await context.env.ASSETS.fetch(url);
+
+	if (!response.ok) {
+		return Response.json({ error: "File not found" }, { status: 404 });
+	}
+
+	const content = await response.arrayBuffer();
+
+	return new Response(content, {
+		headers: {
+			'Content-Type': 'application/octet-stream',
+			'Content-Disposition': 'attachment; filename="SKILL.md"',
+			'Cache-Control': 'public, s-maxage=86400, stale-while-revalidate=3600',
+		}
+	});
+}

functions/api/download/bundle/[provider].js 🔗

@@ -0,0 +1,29 @@
+import { BUNDLE_DOWNLOAD_PROVIDERS } from "../../../../lib/download-providers.js";
+
+export async function onRequestGet(context) {
+	const { provider } = context.params;
+
+	if (!provider || !BUNDLE_DOWNLOAD_PROVIDERS.includes(provider)) {
+		return Response.json({ error: "Invalid provider" }, { status: 400 });
+	}
+
+	const url = new URL(context.request.url);
+	url.pathname = `/_data/dist/${provider}.zip`;
+
+	const response = await context.env.ASSETS.fetch(url);
+
+	if (!response.ok) {
+		return Response.json({ error: "Bundle not found" }, { status: 404 });
+	}
+
+	const content = await response.arrayBuffer();
+	const safeProvider = provider.replace(/[^a-zA-Z0-9._-]/g, '');
+
+	return new Response(content, {
+		headers: {
+			'Content-Type': 'application/zip',
+			'Content-Disposition': `attachment; filename="impeccable-style-${safeProvider}.zip"`,
+			'Cache-Control': 'public, s-maxage=86400, stale-while-revalidate=3600',
+		}
+	});
+}

lib/download-providers.js 🔗

@@ -0,0 +1,24 @@
+export const FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS = Object.freeze({
+  cursor: '.cursor',
+  'claude-code': '.claude',
+  gemini: '.gemini',
+  codex: '.codex',
+  agents: '.agents',
+  kiro: '.kiro',
+  opencode: '.opencode',
+  pi: '.pi',
+});
+
+export const FILE_DOWNLOAD_PROVIDERS = Object.freeze(
+  Object.keys(FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS)
+);
+
+export const BUNDLE_DOWNLOAD_PROVIDERS = Object.freeze([
+  'universal',
+  'universal-prefixed',
+]);
+
+export const DOWNLOAD_PROVIDERS = Object.freeze([
+  ...FILE_DOWNLOAD_PROVIDERS,
+  ...BUNDLE_DOWNLOAD_PROVIDERS,
+]);

package.json 🔗

@@ -0,0 +1,71 @@
+{
+  "name": "impeccable",
+  "version": "2.1.7",
+  "author": "Paul Bakaus",
+  "description": "Design skills, commands, and anti-pattern detection for AI coding agents",
+  "keywords": [
+    "design",
+    "frontend",
+    "ux",
+    "skills",
+    "ai",
+    "anti-patterns",
+    "lint",
+    "accessibility",
+    "css",
+    "html",
+    "detection",
+    "ci-cd"
+  ],
+  "license": "Apache-2.0",
+  "homepage": "https://impeccable.style",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/pbakaus/impeccable.git"
+  },
+  "engines": {
+    "node": ">=18"
+  },
+  "type": "module",
+  "bin": {
+    "impeccable": "bin/cli.js"
+  },
+  "main": "./src/detect-antipatterns.mjs",
+  "exports": {
+    ".": "./src/detect-antipatterns.mjs",
+    "./browser": "./src/detect-antipatterns-browser.js"
+  },
+  "files": [
+    "bin/",
+    "src/",
+    "LICENSE"
+  ],
+  "scripts": {
+    "build": "bun run scripts/build.js",
+    "build:browser": "node scripts/build-browser-detector.js",
+    "build:extension": "node scripts/build-extension.js",
+    "clean": "rm -rf dist build",
+    "rebuild": "bun run clean && bun run build",
+    "dev": "bun run server/index.js",
+    "preview": "bun run build && wrangler pages dev",
+    "deploy": "bun run build && wrangler pages deploy build/",
+    "test": "bun test tests/build.test.js tests/detect-antipatterns.test.js && node --test tests/detect-antipatterns-fixtures.test.mjs && node --test tests/detect-antipatterns-browser.test.mjs && node --test tests/cleanup-deprecated.test.mjs",
+    "prepack": "cp README.md README.repo.md && cp README.npm.md README.md",
+    "postpack": "cp README.repo.md README.md && rm README.repo.md",
+    "screenshot": "bun run scripts/screenshot-antipatterns.js",
+    "og-image": "bun run scripts/generate-og-image.js"
+  },
+  "dependencies": {
+    "jsdom": "^29.0.0",
+    "marked": "^16.1.0"
+  },
+  "optionalDependencies": {
+    "puppeteer": "^24.39.1"
+  },
+  "devDependencies": {
+    "archiver": "^7.0.1",
+    "motion": "^12.38.0",
+    "playwright": "^1.58.2",
+    "wrangler": "^4.75.0"
+  }
+}

public/antipattern-examples/bad-contrast.html 🔗

@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Bad Contrast Choices</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: #1e1e1e;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: linear-gradient(180deg, #3b82f6 0%, #8b5cf6 100%);
+      border-radius: 24px;
+      padding: 48px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      overflow: hidden;
+    }
+
+    /* Header with gray text on colored bg - BAD */
+    .header {
+      text-align: center;
+      margin-bottom: 40px;
+    }
+
+    .header h1 {
+      font-size: 48px;
+      font-weight: 800;
+      color: #808080; /* Gray on blue/purple - very hard to read */
+      margin-bottom: 12px;
+    }
+
+    .header p {
+      font-size: 18px;
+      color: #666666; /* Even worse gray */
+    }
+
+    /* Colored cards with gray bg labels - THE PROBLEM */
+    .cards {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 20px;
+      margin-bottom: 32px;
+    }
+
+    .card {
+      border-radius: 16px;
+      padding: 24px;
+      color: white;
+    }
+
+    .card.blue { background: #2563eb; }
+    .card.green { background: #16a34a; }
+    .card.orange { background: #ea580c; }
+
+    /* Gray background labels on colored cards - looks terrible */
+    .card-label {
+      display: inline-block;
+      background: #9ca3af; /* Gray bg on colored card */
+      color: #4b5563; /* Gray text on gray bg */
+      padding: 4px 10px;
+      border-radius: 4px;
+      font-size: 11px;
+      font-weight: 600;
+      text-transform: uppercase;
+      letter-spacing: 0.5px;
+      margin-bottom: 12px;
+    }
+
+    .card-icon {
+      font-size: 28px;
+      margin-bottom: 12px;
+    }
+
+    .card h3 {
+      font-size: 18px;
+      font-weight: 700;
+      color: #ffffff; /* Pure white - harsh */
+      margin-bottom: 8px;
+    }
+
+    .card p {
+      font-size: 14px;
+      color: #555555; /* Dark gray on colored bg - hard to read */
+      line-height: 1.5;
+    }
+
+    /* Stats section with absolute black bg */
+    .stats {
+      background: #000000; /* Absolute black */
+      border-radius: 16px;
+      padding: 32px;
+      display: flex;
+      justify-content: space-around;
+      margin-bottom: 32px;
+    }
+
+    .stat {
+      text-align: center;
+    }
+
+    .stat-value {
+      font-size: 36px;
+      font-weight: 800;
+      color: #ffffff; /* Pure white */
+    }
+
+    .stat-label {
+      font-size: 14px;
+      color: #444444; /* Dark gray on black - nearly invisible */
+    }
+
+    /* Footer area */
+    .footer {
+      background: #f0f0f0; /* Light gray bg */
+      border-radius: 16px;
+      padding: 28px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+
+    .footer-text {
+      color: #a0a0a0; /* Gray text on gray bg - no contrast */
+      font-size: 14px;
+    }
+
+    .footer-btn {
+      background: #ffffff; /* Pure white btn */
+      color: #000000; /* Pure black text */
+      padding: 12px 24px;
+      border-radius: 8px;
+      font-weight: 600;
+      font-size: 14px;
+      border: none;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="header">
+      <h1>Welcome to Our Platform</h1>
+      <p>The best solution for modern teams and businesses</p>
+    </div>
+
+    <div class="cards">
+      <div class="card blue">
+        <span class="card-label">Popular</span>
+        <div class="card-icon">⚡</div>
+        <h3>Fast Performance</h3>
+        <p>Built for speed with optimized code and efficient algorithms.</p>
+      </div>
+      <div class="card green">
+        <span class="card-label">New</span>
+        <div class="card-icon">🔒</div>
+        <h3>Secure & Safe</h3>
+        <p>Enterprise-grade security with encrypted data storage.</p>
+      </div>
+      <div class="card orange">
+        <span class="card-label">Featured</span>
+        <div class="card-icon">📊</div>
+        <h3>Analytics</h3>
+        <p>Powerful insights to help you make better decisions.</p>
+      </div>
+    </div>
+
+    <div class="stats">
+      <div class="stat">
+        <div class="stat-value">10K+</div>
+        <div class="stat-label">Active Users</div>
+      </div>
+      <div class="stat">
+        <div class="stat-value">99.9%</div>
+        <div class="stat-label">Uptime Guaranteed</div>
+      </div>
+      <div class="stat">
+        <div class="stat-value">50M+</div>
+        <div class="stat-label">Data Processed</div>
+      </div>
+    </div>
+
+    <div class="footer">
+      <span class="footer-text">Ready to get started? Sign up today and join thousands of happy users.</span>
+      <button class="footer-btn">Get Started</button>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/cardocalypse.html 🔗

@@ -0,0 +1,375 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Cardocalypse</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: #f3f4f6;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: #e5e7eb;
+      border-radius: 24px;
+      padding: 32px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+    }
+
+    /* Page card */
+    .page-card {
+      background: #f9fafb;
+      border-radius: 20px;
+      padding: 28px;
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      box-shadow: 0 4px 16px rgba(0,0,0,0.06);
+    }
+
+    /* Header card */
+    .header-card {
+      background: white;
+      border-radius: 16px;
+      padding: 20px 24px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20px;
+      box-shadow: 0 2px 8px rgba(0,0,0,0.04);
+    }
+
+    .logo-card {
+      background: #f3f4f6;
+      border-radius: 10px;
+      padding: 10px 16px;
+      font-weight: 700;
+      font-size: 16px;
+      color: #374151;
+    }
+
+    .nav-card {
+      background: #f3f4f6;
+      border-radius: 10px;
+      padding: 8px;
+      display: flex;
+      gap: 4px;
+    }
+
+    .nav-item-card {
+      background: white;
+      border-radius: 8px;
+      padding: 8px 16px;
+      font-size: 13px;
+      color: #6b7280;
+      font-weight: 500;
+    }
+
+    .nav-item-card.active {
+      background: #111;
+      color: white;
+    }
+
+    /* Main content area */
+    .content-card {
+      background: white;
+      border-radius: 16px;
+      padding: 24px;
+      flex: 1;
+      display: flex;
+      gap: 20px;
+      box-shadow: 0 2px 8px rgba(0,0,0,0.04);
+    }
+
+    /* Sidebar card */
+    .sidebar-card {
+      background: #f9fafb;
+      border-radius: 14px;
+      padding: 16px;
+      width: 200px;
+      display: flex;
+      flex-direction: column;
+      gap: 8px;
+    }
+
+    .sidebar-item-card {
+      background: white;
+      border-radius: 10px;
+      padding: 12px 14px;
+      font-size: 13px;
+      color: #6b7280;
+      font-weight: 500;
+      box-shadow: 0 1px 3px rgba(0,0,0,0.04);
+    }
+
+    .sidebar-item-card.active {
+      background: #eef2ff;
+      color: #4f46e5;
+      border: 1px solid #c7d2fe;
+    }
+
+    /* Main panel card */
+    .main-panel-card {
+      background: #f9fafb;
+      border-radius: 14px;
+      padding: 20px;
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      gap: 16px;
+    }
+
+    .section-title-card {
+      background: white;
+      border-radius: 10px;
+      padding: 14px 18px;
+      font-weight: 600;
+      font-size: 15px;
+      color: #1f2937;
+      box-shadow: 0 1px 3px rgba(0,0,0,0.04);
+    }
+
+    /* Stats row */
+    .stats-row-card {
+      background: white;
+      border-radius: 12px;
+      padding: 16px;
+      display: flex;
+      gap: 12px;
+      box-shadow: 0 1px 3px rgba(0,0,0,0.04);
+    }
+
+    .stat-card {
+      background: #f9fafb;
+      border-radius: 10px;
+      padding: 16px;
+      flex: 1;
+      text-align: center;
+    }
+
+    .stat-value-card {
+      background: white;
+      border-radius: 8px;
+      padding: 8px;
+      font-size: 24px;
+      font-weight: 700;
+      color: #111;
+      margin-bottom: 8px;
+      box-shadow: 0 1px 2px rgba(0,0,0,0.04);
+    }
+
+    .stat-label {
+      font-size: 11px;
+      color: #9ca3af;
+      text-transform: uppercase;
+      letter-spacing: 0.5px;
+    }
+
+    /* List card */
+    .list-card {
+      background: white;
+      border-radius: 12px;
+      padding: 16px;
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      gap: 10px;
+      box-shadow: 0 1px 3px rgba(0,0,0,0.04);
+    }
+
+    .list-item-card {
+      background: #f9fafb;
+      border-radius: 10px;
+      padding: 14px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+
+    .list-item-info-card {
+      background: white;
+      border-radius: 8px;
+      padding: 10px 14px;
+      display: flex;
+      align-items: center;
+      gap: 12px;
+      box-shadow: 0 1px 2px rgba(0,0,0,0.04);
+    }
+
+    .avatar-card {
+      background: #e5e7eb;
+      border-radius: 6px;
+      width: 32px;
+      height: 32px;
+    }
+
+    .list-item-name {
+      font-size: 13px;
+      font-weight: 600;
+      color: #374151;
+    }
+
+    .list-item-actions-card {
+      background: white;
+      border-radius: 8px;
+      padding: 8px;
+      display: flex;
+      gap: 4px;
+      box-shadow: 0 1px 2px rgba(0,0,0,0.04);
+    }
+
+    .action-btn-card {
+      background: #f3f4f6;
+      border-radius: 6px;
+      padding: 6px 12px;
+      font-size: 11px;
+      color: #6b7280;
+      font-weight: 500;
+    }
+
+    /* Nesting depth indicator */
+    .depth-legend {
+      position: absolute;
+      top: 48px;
+      right: 48px;
+      background: white;
+      border-radius: 12px;
+      padding: 16px;
+      box-shadow: 0 4px 12px rgba(0,0,0,0.1);
+    }
+
+    .depth-title {
+      font-size: 11px;
+      font-weight: 600;
+      color: #6b7280;
+      margin-bottom: 10px;
+      text-transform: uppercase;
+      letter-spacing: 0.5px;
+    }
+
+    .depth-item {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      margin-bottom: 6px;
+      font-size: 11px;
+      color: #374151;
+    }
+
+    .depth-box {
+      width: 16px;
+      height: 16px;
+      border-radius: 4px;
+    }
+
+    .depth-1 { background: #e5e7eb; }
+    .depth-2 { background: #f9fafb; }
+    .depth-3 { background: #ffffff; }
+    .depth-4 { background: #f9fafb; border: 1px solid #e5e7eb; }
+    .depth-5 { background: #ffffff; border: 1px solid #e5e7eb; }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="depth-legend">
+      <div class="depth-title">Nesting Depth</div>
+      <div class="depth-item"><div class="depth-box depth-1"></div> Level 1</div>
+      <div class="depth-item"><div class="depth-box depth-2"></div> Level 2</div>
+      <div class="depth-item"><div class="depth-box depth-3"></div> Level 3</div>
+      <div class="depth-item"><div class="depth-box depth-4"></div> Level 4</div>
+      <div class="depth-item"><div class="depth-box depth-5"></div> Level 5</div>
+    </div>
+
+    <div class="page-card">
+      <div class="header-card">
+        <div class="logo-card">Dashboard</div>
+        <div class="nav-card">
+          <div class="nav-item-card active">Overview</div>
+          <div class="nav-item-card">Analytics</div>
+          <div class="nav-item-card">Settings</div>
+        </div>
+      </div>
+
+      <div class="content-card">
+        <div class="sidebar-card">
+          <div class="sidebar-item-card active">Dashboard</div>
+          <div class="sidebar-item-card">Projects</div>
+          <div class="sidebar-item-card">Team</div>
+          <div class="sidebar-item-card">Reports</div>
+        </div>
+
+        <div class="main-panel-card">
+          <div class="section-title-card">Overview</div>
+
+          <div class="stats-row-card">
+            <div class="stat-card">
+              <div class="stat-value-card">2,847</div>
+              <div class="stat-label">Total Users</div>
+            </div>
+            <div class="stat-card">
+              <div class="stat-value-card">$48.2K</div>
+              <div class="stat-label">Revenue</div>
+            </div>
+            <div class="stat-card">
+              <div class="stat-value-card">94.2%</div>
+              <div class="stat-label">Uptime</div>
+            </div>
+          </div>
+
+          <div class="list-card">
+            <div class="list-item-card">
+              <div class="list-item-info-card">
+                <div class="avatar-card"></div>
+                <span class="list-item-name">John Smith</span>
+              </div>
+              <div class="list-item-actions-card">
+                <div class="action-btn-card">Edit</div>
+                <div class="action-btn-card">Delete</div>
+              </div>
+            </div>
+            <div class="list-item-card">
+              <div class="list-item-info-card">
+                <div class="avatar-card"></div>
+                <span class="list-item-name">Sarah Johnson</span>
+              </div>
+              <div class="list-item-actions-card">
+                <div class="action-btn-card">Edit</div>
+                <div class="action-btn-card">Delete</div>
+              </div>
+            </div>
+            <div class="list-item-card">
+              <div class="list-item-info-card">
+                <div class="avatar-card"></div>
+                <span class="list-item-name">Mike Davis</span>
+              </div>
+              <div class="list-item-actions-card">
+                <div class="action-btn-card">Edit</div>
+                <div class="action-btn-card">Delete</div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/inter-everywhere.html 🔗

@@ -0,0 +1,317 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Inter Font Everywhere</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
+      min-height: 100vh;
+      background: #f5f5f5;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: #ffffff;
+      border-radius: 24px;
+      padding: 48px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      box-shadow: 0 4px 24px rgba(0,0,0,0.08);
+    }
+
+    /* Navigation */
+    nav {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 48px;
+    }
+
+    .logo {
+      font-size: 20px;
+      font-weight: 700;
+      color: #111;
+      letter-spacing: -0.5px;
+    }
+
+    .nav-links {
+      display: flex;
+      gap: 32px;
+      align-items: center;
+    }
+
+    .nav-links a {
+      color: #666;
+      text-decoration: none;
+      font-size: 14px;
+      font-weight: 500;
+    }
+
+    .nav-cta {
+      background: #111;
+      color: #fff !important;
+      padding: 10px 20px;
+      border-radius: 8px;
+      font-weight: 600;
+    }
+
+    /* Hero */
+    .hero {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      max-width: 700px;
+    }
+
+    .badge {
+      display: inline-flex;
+      align-items: center;
+      gap: 8px;
+      background: #f0f0f0;
+      padding: 6px 12px;
+      border-radius: 6px;
+      font-size: 12px;
+      font-weight: 600;
+      color: #666;
+      width: fit-content;
+      margin-bottom: 24px;
+      text-transform: uppercase;
+      letter-spacing: 0.5px;
+    }
+
+    h1 {
+      font-size: 56px;
+      font-weight: 800;
+      line-height: 1.1;
+      margin-bottom: 20px;
+      color: #111;
+      letter-spacing: -2px;
+    }
+
+    .subtitle {
+      font-size: 18px;
+      color: #666;
+      line-height: 1.6;
+      margin-bottom: 32px;
+      font-weight: 400;
+    }
+
+    .cta-group {
+      display: flex;
+      gap: 12px;
+    }
+
+    .btn-primary {
+      background: #111;
+      color: white;
+      padding: 14px 28px;
+      border-radius: 8px;
+      font-weight: 600;
+      font-size: 15px;
+      border: none;
+      cursor: pointer;
+    }
+
+    .btn-secondary {
+      background: #f5f5f5;
+      border: 1px solid #e0e0e0;
+      color: #333;
+      padding: 14px 28px;
+      border-radius: 8px;
+      font-weight: 600;
+      font-size: 15px;
+      cursor: pointer;
+    }
+
+    /* Features */
+    .features {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 24px;
+      margin-top: 48px;
+      padding-top: 40px;
+      border-top: 1px solid #eee;
+    }
+
+    .feature {
+      padding: 20px;
+    }
+
+    .feature-icon {
+      width: 40px;
+      height: 40px;
+      background: #f5f5f5;
+      border-radius: 8px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin-bottom: 16px;
+      font-size: 18px;
+    }
+
+    .feature h3 {
+      font-size: 15px;
+      font-weight: 700;
+      margin-bottom: 8px;
+      color: #111;
+      letter-spacing: -0.3px;
+    }
+
+    .feature p {
+      font-size: 13px;
+      color: #888;
+      line-height: 1.5;
+      font-weight: 400;
+    }
+
+    /* Testimonial */
+    .testimonial {
+      background: #fafafa;
+      border-radius: 12px;
+      padding: 24px;
+      margin-top: 24px;
+    }
+
+    .testimonial-text {
+      font-size: 14px;
+      color: #444;
+      line-height: 1.6;
+      font-style: italic;
+      margin-bottom: 16px;
+    }
+
+    .testimonial-author {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+    }
+
+    .testimonial-avatar {
+      width: 36px;
+      height: 36px;
+      background: #ddd;
+      border-radius: 50%;
+    }
+
+    .testimonial-name {
+      font-size: 13px;
+      font-weight: 600;
+      color: #111;
+    }
+
+    .testimonial-role {
+      font-size: 12px;
+      color: #888;
+      font-weight: 400;
+    }
+
+    /* Font specimen overlay */
+    .font-specimen {
+      position: absolute;
+      top: 48px;
+      right: 48px;
+      background: #111;
+      color: #fff;
+      padding: 16px 24px;
+      border-radius: 10px;
+      text-align: center;
+    }
+
+    .font-specimen-label {
+      font-size: 10px;
+      text-transform: uppercase;
+      letter-spacing: 1px;
+      color: #888;
+      margin-bottom: 4px;
+    }
+
+    .font-specimen-name {
+      font-size: 28px;
+      font-weight: 800;
+      letter-spacing: -1px;
+    }
+
+    .font-specimen-weights {
+      font-size: 9px;
+      color: #666;
+      margin-top: 6px;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <nav>
+      <div class="logo">genericstartup</div>
+      <div class="nav-links">
+        <a href="#">Product</a>
+        <a href="#">Features</a>
+        <a href="#">Pricing</a>
+        <a href="#" class="nav-cta">Sign Up</a>
+      </div>
+    </nav>
+
+    <div class="font-specimen">
+      <div class="font-specimen-label">Font Used</div>
+      <div class="font-specimen-name">Inter</div>
+      <div class="font-specimen-weights">400 · 500 · 600 · 700 · 800</div>
+    </div>
+
+    <div class="hero">
+      <span class="badge">Now Available</span>
+      <h1>The platform for modern teams</h1>
+      <p class="subtitle">Streamline your workflow with our all-in-one solution. Built for teams who want to move fast and ship faster.</p>
+      <div class="cta-group">
+        <button class="btn-primary">Get Started Free</button>
+        <button class="btn-secondary">Book a Demo</button>
+      </div>
+    </div>
+
+    <div class="features">
+      <div class="feature">
+        <div class="feature-icon">⚡</div>
+        <h3>Lightning Fast</h3>
+        <p>Built for speed from the ground up. Everything loads instantly.</p>
+      </div>
+      <div class="feature">
+        <div class="feature-icon">🔒</div>
+        <h3>Secure by Default</h3>
+        <p>Enterprise-grade security with end-to-end encryption.</p>
+      </div>
+      <div class="feature">
+        <div class="feature-icon">🔗</div>
+        <h3>Integrations</h3>
+        <p>Connect with 100+ tools your team already uses.</p>
+      </div>
+    </div>
+
+    <div class="testimonial">
+      <p class="testimonial-text">"This product has completely transformed how our team works. We've seen a 40% increase in productivity."</p>
+      <div class="testimonial-author">
+        <div class="testimonial-avatar"></div>
+        <div>
+          <div class="testimonial-name">Sarah Johnson</div>
+          <div class="testimonial-role">Head of Product, TechCorp</div>
+        </div>
+      </div>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/layout-templates.html 🔗

@@ -0,0 +1,322 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Layout Templates</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: #f5f5f5;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: #ffffff;
+      border-radius: 24px;
+      padding: 32px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      box-shadow: 0 4px 24px rgba(0,0,0,0.08);
+    }
+
+    /* Template indicator */
+    .template-badge {
+      position: absolute;
+      top: 24px;
+      right: 24px;
+      background: #fef3c7;
+      color: #92400e;
+      padding: 6px 12px;
+      border-radius: 6px;
+      font-size: 11px;
+      font-weight: 600;
+      border: 1px solid #fcd34d;
+    }
+
+    /* THE TEMPLATE: Hero Metric Layout */
+    .hero-metric {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      border-radius: 20px;
+      padding: 40px;
+      margin-bottom: 24px;
+      color: white;
+      position: relative;
+      overflow: hidden;
+    }
+
+    .hero-metric::before {
+      content: '';
+      position: absolute;
+      top: -50%;
+      right: -50%;
+      width: 100%;
+      height: 100%;
+      background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
+    }
+
+    .hero-metric-content {
+      position: relative;
+      z-index: 1;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+
+    .hero-metric-main {
+      text-align: left;
+    }
+
+    .hero-metric-label {
+      font-size: 14px;
+      text-transform: uppercase;
+      letter-spacing: 1px;
+      opacity: 0.8;
+      margin-bottom: 8px;
+    }
+
+    .hero-metric-value {
+      font-size: 72px;
+      font-weight: 900;
+      line-height: 1;
+      margin-bottom: 8px;
+    }
+
+    .hero-metric-change {
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+      background: rgba(255,255,255,0.2);
+      padding: 6px 12px;
+      border-radius: 6px;
+      font-size: 14px;
+      font-weight: 600;
+    }
+
+    .hero-metric-supporting {
+      display: flex;
+      gap: 32px;
+    }
+
+    .supporting-stat {
+      text-align: right;
+    }
+
+    .supporting-stat-value {
+      font-size: 28px;
+      font-weight: 700;
+      margin-bottom: 4px;
+    }
+
+    .supporting-stat-label {
+      font-size: 12px;
+      opacity: 0.7;
+      text-transform: uppercase;
+      letter-spacing: 0.5px;
+    }
+
+    /* Repeat the same template 3 more times */
+    .metrics-grid {
+      display: grid;
+      grid-template-columns: repeat(2, 1fr);
+      gap: 20px;
+      flex: 1;
+    }
+
+    .metric-template {
+      border-radius: 16px;
+      padding: 28px;
+      color: white;
+      position: relative;
+      overflow: hidden;
+    }
+
+    .metric-template::before {
+      content: '';
+      position: absolute;
+      top: -30%;
+      right: -30%;
+      width: 60%;
+      height: 60%;
+      background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
+    }
+
+    .metric-template.blue { background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); }
+    .metric-template.green { background: linear-gradient(135deg, #22c55e 0%, #15803d 100%); }
+    .metric-template.orange { background: linear-gradient(135deg, #f97316 0%, #c2410c 100%); }
+    .metric-template.pink { background: linear-gradient(135deg, #ec4899 0%, #be185d 100%); }
+
+    .metric-template-content {
+      position: relative;
+      z-index: 1;
+    }
+
+    .metric-template-label {
+      font-size: 12px;
+      text-transform: uppercase;
+      letter-spacing: 1px;
+      opacity: 0.8;
+      margin-bottom: 8px;
+    }
+
+    .metric-template-value {
+      font-size: 42px;
+      font-weight: 800;
+      line-height: 1;
+      margin-bottom: 8px;
+    }
+
+    .metric-template-change {
+      display: inline-flex;
+      align-items: center;
+      gap: 4px;
+      background: rgba(255,255,255,0.2);
+      padding: 4px 10px;
+      border-radius: 4px;
+      font-size: 12px;
+      font-weight: 600;
+      margin-bottom: 16px;
+    }
+
+    .metric-template-row {
+      display: flex;
+      gap: 20px;
+      padding-top: 16px;
+      border-top: 1px solid rgba(255,255,255,0.2);
+    }
+
+    .metric-template-sub {
+      flex: 1;
+    }
+
+    .metric-template-sub-value {
+      font-size: 18px;
+      font-weight: 700;
+      margin-bottom: 2px;
+    }
+
+    .metric-template-sub-label {
+      font-size: 10px;
+      opacity: 0.7;
+      text-transform: uppercase;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="template-badge">Same layout × 5</div>
+
+    <!-- THE TEMPLATE: Hero Metric Layout -->
+    <div class="hero-metric">
+      <div class="hero-metric-content">
+        <div class="hero-metric-main">
+          <div class="hero-metric-label">Total Revenue</div>
+          <div class="hero-metric-value">$2.4M</div>
+          <div class="hero-metric-change">↑ +24.5% from last month</div>
+        </div>
+        <div class="hero-metric-supporting">
+          <div class="supporting-stat">
+            <div class="supporting-stat-value">12.8K</div>
+            <div class="supporting-stat-label">Customers</div>
+          </div>
+          <div class="supporting-stat">
+            <div class="supporting-stat-value">$187</div>
+            <div class="supporting-stat-label">Avg. Order</div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Same template repeated 4 more times -->
+    <div class="metrics-grid">
+      <div class="metric-template blue">
+        <div class="metric-template-content">
+          <div class="metric-template-label">Active Users</div>
+          <div class="metric-template-value">48.2K</div>
+          <div class="metric-template-change">↑ +12.3%</div>
+          <div class="metric-template-row">
+            <div class="metric-template-sub">
+              <div class="metric-template-sub-value">32.1K</div>
+              <div class="metric-template-sub-label">Mobile</div>
+            </div>
+            <div class="metric-template-sub">
+              <div class="metric-template-sub-value">16.1K</div>
+              <div class="metric-template-sub-label">Desktop</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="metric-template green">
+        <div class="metric-template-content">
+          <div class="metric-template-label">Conversion Rate</div>
+          <div class="metric-template-value">8.7%</div>
+          <div class="metric-template-change">↑ +2.1%</div>
+          <div class="metric-template-row">
+            <div class="metric-template-sub">
+              <div class="metric-template-sub-value">12.4%</div>
+              <div class="metric-template-sub-label">Returning</div>
+            </div>
+            <div class="metric-template-sub">
+              <div class="metric-template-sub-value">5.2%</div>
+              <div class="metric-template-sub-label">New</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="metric-template orange">
+        <div class="metric-template-content">
+          <div class="metric-template-label">Avg. Session</div>
+          <div class="metric-template-value">4:32</div>
+          <div class="metric-template-change">↓ -0.8%</div>
+          <div class="metric-template-row">
+            <div class="metric-template-sub">
+              <div class="metric-template-sub-value">5.2</div>
+              <div class="metric-template-sub-label">Pages/Visit</div>
+            </div>
+            <div class="metric-template-sub">
+              <div class="metric-template-sub-value">32%</div>
+              <div class="metric-template-sub-label">Bounce</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="metric-template pink">
+        <div class="metric-template-content">
+          <div class="metric-template-label">NPS Score</div>
+          <div class="metric-template-value">72</div>
+          <div class="metric-template-change">↑ +5 pts</div>
+          <div class="metric-template-row">
+            <div class="metric-template-sub">
+              <div class="metric-template-sub-value">84%</div>
+              <div class="metric-template-sub-label">Promoters</div>
+            </div>
+            <div class="metric-template-sub">
+              <div class="metric-template-sub-value">4%</div>
+              <div class="metric-template-sub-label">Detractors</div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/lazy-cool.html 🔗

@@ -0,0 +1,320 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Lazy "Cool" Design</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: #0a0a0a;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: linear-gradient(135deg, #0f0f23 0%, #0a0a1a 100%);
+      border-radius: 24px;
+      padding: 48px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      overflow: hidden;
+    }
+
+    /* Glow orbs in background */
+    .glow-orb {
+      position: absolute;
+      border-radius: 50%;
+      filter: blur(100px);
+      opacity: 0.4;
+    }
+
+    .glow-1 {
+      width: 400px;
+      height: 400px;
+      background: #00ffff;
+      top: -100px;
+      right: -100px;
+    }
+
+    .glow-2 {
+      width: 300px;
+      height: 300px;
+      background: #ff00ff;
+      bottom: -50px;
+      left: -50px;
+    }
+
+    /* Content */
+    .content {
+      position: relative;
+      z-index: 1;
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+    }
+
+    /* Nav with glassmorphism */
+    nav {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 48px;
+      background: rgba(255, 255, 255, 0.05);
+      backdrop-filter: blur(20px);
+      -webkit-backdrop-filter: blur(20px);
+      border: 1px solid rgba(255, 255, 255, 0.1);
+      border-radius: 16px;
+      padding: 16px 24px;
+    }
+
+    .logo {
+      font-family: 'JetBrains Mono', monospace;
+      font-size: 20px;
+      font-weight: 700;
+      color: #00ffff;
+      text-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
+    }
+
+    .nav-links {
+      display: flex;
+      gap: 24px;
+    }
+
+    .nav-links a {
+      font-family: 'JetBrains Mono', monospace;
+      color: rgba(255, 255, 255, 0.6);
+      text-decoration: none;
+      font-size: 13px;
+      text-transform: uppercase;
+      letter-spacing: 1px;
+    }
+
+    .nav-cta {
+      background: linear-gradient(135deg, #00ffff, #00ccff) !important;
+      color: #000 !important;
+      padding: 10px 20px;
+      border-radius: 8px;
+      font-weight: 600;
+      box-shadow: 0 0 30px rgba(0, 255, 255, 0.4);
+    }
+
+    /* Hero with neon */
+    .hero {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      text-align: center;
+      padding: 0 60px;
+    }
+
+    .hero-badge {
+      font-family: 'JetBrains Mono', monospace;
+      display: inline-flex;
+      align-items: center;
+      gap: 8px;
+      background: rgba(0, 255, 255, 0.1);
+      border: 1px solid rgba(0, 255, 255, 0.3);
+      padding: 8px 16px;
+      border-radius: 999px;
+      font-size: 12px;
+      margin: 0 auto 24px;
+      color: #00ffff;
+      text-transform: uppercase;
+      letter-spacing: 2px;
+    }
+
+    h1 {
+      font-size: 56px;
+      font-weight: 800;
+      line-height: 1.1;
+      margin-bottom: 24px;
+      color: #ffffff;
+      text-shadow: 0 0 40px rgba(0, 255, 255, 0.3);
+    }
+
+    h1 span {
+      color: #00ffff;
+      text-shadow: 0 0 30px rgba(0, 255, 255, 0.8);
+    }
+
+    .subtitle {
+      font-family: 'JetBrains Mono', monospace;
+      font-size: 16px;
+      color: rgba(255, 255, 255, 0.5);
+      max-width: 500px;
+      margin: 0 auto 40px;
+      line-height: 1.7;
+    }
+
+    .cta-group {
+      display: flex;
+      gap: 16px;
+      justify-content: center;
+    }
+
+    .btn-primary {
+      font-family: 'JetBrains Mono', monospace;
+      background: linear-gradient(135deg, #00ffff 0%, #00ccff 100%);
+      color: #000;
+      padding: 16px 32px;
+      border-radius: 12px;
+      font-weight: 600;
+      font-size: 14px;
+      border: none;
+      cursor: pointer;
+      box-shadow: 0 0 40px rgba(0, 255, 255, 0.5);
+      text-transform: uppercase;
+      letter-spacing: 1px;
+    }
+
+    .btn-secondary {
+      font-family: 'JetBrains Mono', monospace;
+      background: rgba(255, 255, 255, 0.05);
+      backdrop-filter: blur(10px);
+      border: 1px solid rgba(0, 255, 255, 0.3);
+      color: #00ffff;
+      padding: 16px 32px;
+      border-radius: 12px;
+      font-weight: 600;
+      font-size: 14px;
+      cursor: pointer;
+      text-transform: uppercase;
+      letter-spacing: 1px;
+    }
+
+    /* Glass cards */
+    .cards {
+      display: flex;
+      gap: 20px;
+      margin-top: 48px;
+    }
+
+    .glass-card {
+      flex: 1;
+      background: rgba(255, 255, 255, 0.03);
+      backdrop-filter: blur(20px);
+      -webkit-backdrop-filter: blur(20px);
+      border: 1px solid rgba(255, 255, 255, 0.08);
+      border-radius: 16px;
+      padding: 24px;
+      text-align: center;
+    }
+
+    .glass-card-icon {
+      font-size: 28px;
+      margin-bottom: 12px;
+      filter: drop-shadow(0 0 10px rgba(0, 255, 255, 0.5));
+    }
+
+    .glass-card h3 {
+      font-family: 'JetBrains Mono', monospace;
+      font-size: 14px;
+      color: #00ffff;
+      margin-bottom: 8px;
+      text-transform: uppercase;
+      letter-spacing: 1px;
+    }
+
+    .glass-card p {
+      font-size: 13px;
+      color: rgba(255, 255, 255, 0.4);
+      line-height: 1.5;
+    }
+
+    /* Terminal-style element */
+    .terminal {
+      position: absolute;
+      top: 48px;
+      right: 48px;
+      background: rgba(0, 0, 0, 0.6);
+      border: 1px solid rgba(0, 255, 255, 0.2);
+      border-radius: 8px;
+      padding: 12px 16px;
+      font-family: 'JetBrains Mono', monospace;
+      font-size: 11px;
+    }
+
+    .terminal-header {
+      color: rgba(255, 255, 255, 0.4);
+      margin-bottom: 8px;
+    }
+
+    .terminal-line {
+      color: #00ffff;
+    }
+
+    .terminal-line::before {
+      content: '> ';
+      color: #ff00ff;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="glow-orb glow-1"></div>
+    <div class="glow-orb glow-2"></div>
+
+    <div class="terminal">
+      <div class="terminal-header">~/hacker-vibes</div>
+      <div class="terminal-line">npm run look-cool</div>
+    </div>
+
+    <div class="content">
+      <nav>
+        <div class="logo">&lt;CYBER/&gt;</div>
+        <div class="nav-links">
+          <a href="#">Features</a>
+          <a href="#">Docs</a>
+          <a href="#">Pricing</a>
+          <a href="#" class="nav-cta">Launch</a>
+        </div>
+      </nav>
+
+      <div class="hero">
+        <span class="hero-badge">// Now in Beta</span>
+        <h1>Build the Future<br>with <span>Cyber Tech</span></h1>
+        <p class="subtitle">const future = await buildWithAI(); // Neon glow makes everything 10x more futuristic</p>
+        <div class="cta-group">
+          <button class="btn-primary">Initialize_</button>
+          <button class="btn-secondary">View_Code</button>
+        </div>
+      </div>
+
+      <div class="cards">
+        <div class="glass-card">
+          <div class="glass-card-icon">⚡</div>
+          <h3>Lightning</h3>
+          <p>Blazing fast with quantum acceleration</p>
+        </div>
+        <div class="glass-card">
+          <div class="glass-card-icon">🔮</div>
+          <h3>AI Powered</h3>
+          <p>Neural networks do the heavy lifting</p>
+        </div>
+        <div class="glass-card">
+          <div class="glass-card-icon">🌐</div>
+          <h3>Web3 Ready</h3>
+          <p>Decentralized by default</p>
+        </div>
+      </div>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/lazy-impact.html 🔗

@@ -0,0 +1,378 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Lazy "Impact" Design</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: #0f0f0f;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: linear-gradient(180deg, #1a1a2e 0%, #16213e 50%, #0f0f23 100%);
+      border-radius: 24px;
+      padding: 48px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      overflow: hidden;
+    }
+
+    /* Animated gradient background */
+    .bg-gradient {
+      position: absolute;
+      inset: 0;
+      background:
+        radial-gradient(circle at 20% 20%, rgba(139, 92, 246, 0.15) 0%, transparent 50%),
+        radial-gradient(circle at 80% 80%, rgba(236, 72, 153, 0.15) 0%, transparent 50%),
+        radial-gradient(circle at 50% 50%, rgba(59, 130, 246, 0.1) 0%, transparent 70%);
+      animation: pulse 8s ease-in-out infinite;
+    }
+
+    @keyframes pulse {
+      0%, 100% { opacity: 0.5; }
+      50% { opacity: 1; }
+    }
+
+    .content {
+      position: relative;
+      z-index: 1;
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+    }
+
+    /* Hero with gradient text */
+    .hero {
+      text-align: center;
+      margin-bottom: 40px;
+    }
+
+    .hero-label {
+      display: inline-flex;
+      align-items: center;
+      gap: 8px;
+      background: linear-gradient(135deg, rgba(139, 92, 246, 0.2), rgba(236, 72, 153, 0.2));
+      border: 1px solid rgba(139, 92, 246, 0.3);
+      padding: 8px 20px;
+      border-radius: 999px;
+      font-size: 13px;
+      color: #c4b5fd;
+      margin-bottom: 24px;
+      animation: float 3s ease-in-out infinite;
+    }
+
+    @keyframes float {
+      0%, 100% { transform: translateY(0); }
+      50% { transform: translateY(-5px); }
+    }
+
+    h1 {
+      font-size: 64px;
+      font-weight: 900;
+      line-height: 1.0;
+      margin-bottom: 16px;
+      background: linear-gradient(135deg, #ffffff 0%, #a78bfa 50%, #ec4899 100%);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      animation: gradient-shift 5s ease infinite;
+      background-size: 200% 200%;
+    }
+
+    @keyframes gradient-shift {
+      0% { background-position: 0% 50%; }
+      50% { background-position: 100% 50%; }
+      100% { background-position: 0% 50%; }
+    }
+
+    .subtitle {
+      font-size: 18px;
+      color: rgba(255, 255, 255, 0.5);
+      margin-bottom: 32px;
+    }
+
+    /* Metrics with sparklines */
+    .metrics {
+      display: flex;
+      gap: 24px;
+      margin-bottom: 32px;
+    }
+
+    .metric-card {
+      flex: 1;
+      background: rgba(255, 255, 255, 0.03);
+      border: 1px solid rgba(255, 255, 255, 0.08);
+      border-radius: 16px;
+      padding: 24px;
+      animation: card-pop 0.5s ease-out;
+    }
+
+    @keyframes card-pop {
+      0% { transform: scale(0.9); opacity: 0; }
+      100% { transform: scale(1); opacity: 1; }
+    }
+
+    .metric-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+      margin-bottom: 12px;
+    }
+
+    .metric-label {
+      font-size: 13px;
+      color: rgba(255, 255, 255, 0.4);
+      font-weight: 500;
+    }
+
+    .metric-change {
+      font-size: 12px;
+      font-weight: 600;
+      padding: 4px 8px;
+      border-radius: 6px;
+      animation: bounce 2s ease-in-out infinite;
+    }
+
+    @keyframes bounce {
+      0%, 100% { transform: translateY(0); }
+      50% { transform: translateY(-3px); }
+    }
+
+    .metric-change.positive {
+      background: rgba(34, 197, 94, 0.2);
+      color: #4ade80;
+    }
+
+    .metric-change.negative {
+      background: rgba(239, 68, 68, 0.2);
+      color: #f87171;
+    }
+
+    .metric-value {
+      font-size: 36px;
+      font-weight: 800;
+      background: linear-gradient(135deg, #fff, #a78bfa);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      margin-bottom: 16px;
+    }
+
+    /* SVG Sparkline */
+    .sparkline {
+      width: 100%;
+      height: 40px;
+    }
+
+    .sparkline-path {
+      fill: none;
+      stroke: url(#sparkline-gradient);
+      stroke-width: 2;
+      stroke-linecap: round;
+      stroke-linejoin: round;
+    }
+
+    .sparkline-area {
+      fill: url(#sparkline-area-gradient);
+    }
+
+    /* CTA with elastic animation */
+    .cta-section {
+      display: flex;
+      gap: 16px;
+      justify-content: center;
+      margin-bottom: 40px;
+    }
+
+    .btn-primary {
+      background: linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%);
+      color: white;
+      padding: 16px 40px;
+      border-radius: 12px;
+      font-weight: 700;
+      font-size: 16px;
+      border: none;
+      cursor: pointer;
+      box-shadow: 0 0 40px rgba(139, 92, 246, 0.4);
+      animation: elastic 2s ease-in-out infinite;
+      position: relative;
+      overflow: hidden;
+    }
+
+    @keyframes elastic {
+      0%, 100% { transform: scale(1); }
+      25% { transform: scale(1.05); }
+      50% { transform: scale(0.98); }
+      75% { transform: scale(1.02); }
+    }
+
+    .btn-primary::after {
+      content: '';
+      position: absolute;
+      inset: 0;
+      background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.3) 50%, transparent 70%);
+      animation: shimmer 2s ease-in-out infinite;
+    }
+
+    @keyframes shimmer {
+      0% { transform: translateX(-100%); }
+      100% { transform: translateX(100%); }
+    }
+
+    /* Features grid */
+    .features {
+      display: grid;
+      grid-template-columns: repeat(4, 1fr);
+      gap: 16px;
+      flex: 1;
+    }
+
+    .feature {
+      background: linear-gradient(180deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%);
+      border: 1px solid rgba(255, 255, 255, 0.06);
+      border-radius: 12px;
+      padding: 20px;
+      text-align: center;
+      transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
+    }
+
+    .feature:hover {
+      transform: translateY(-8px) scale(1.02);
+      box-shadow: 0 20px 40px rgba(139, 92, 246, 0.2);
+    }
+
+    .feature-icon {
+      font-size: 28px;
+      margin-bottom: 12px;
+      animation: wiggle 3s ease-in-out infinite;
+    }
+
+    @keyframes wiggle {
+      0%, 100% { transform: rotate(0deg); }
+      25% { transform: rotate(5deg); }
+      75% { transform: rotate(-5deg); }
+    }
+
+    .feature h3 {
+      font-size: 14px;
+      font-weight: 700;
+      background: linear-gradient(135deg, #fff, #c4b5fd);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      margin-bottom: 6px;
+    }
+
+    .feature p {
+      font-size: 11px;
+      color: rgba(255, 255, 255, 0.4);
+      line-height: 1.4;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="bg-gradient"></div>
+
+    <svg width="0" height="0">
+      <defs>
+        <linearGradient id="sparkline-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
+          <stop offset="0%" stop-color="#8b5cf6" />
+          <stop offset="100%" stop-color="#ec4899" />
+        </linearGradient>
+        <linearGradient id="sparkline-area-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
+          <stop offset="0%" stop-color="rgba(139, 92, 246, 0.3)" />
+          <stop offset="100%" stop-color="rgba(139, 92, 246, 0)" />
+        </linearGradient>
+      </defs>
+    </svg>
+
+    <div class="content">
+      <div class="hero">
+        <span class="hero-label">✨ Launching Soon</span>
+        <h1>10X Your Growth</h1>
+        <p class="subtitle">Transform your metrics with AI-powered insights</p>
+      </div>
+
+      <div class="metrics">
+        <div class="metric-card">
+          <div class="metric-header">
+            <span class="metric-label">Revenue</span>
+            <span class="metric-change positive">+127%</span>
+          </div>
+          <div class="metric-value">$2.4M</div>
+          <svg class="sparkline" viewBox="0 0 200 40">
+            <path class="sparkline-area" d="M0,35 L20,30 L40,32 L60,25 L80,28 L100,20 L120,22 L140,15 L160,18 L180,10 L200,5 L200,40 L0,40 Z"/>
+            <path class="sparkline-path" d="M0,35 L20,30 L40,32 L60,25 L80,28 L100,20 L120,22 L140,15 L160,18 L180,10 L200,5"/>
+          </svg>
+        </div>
+        <div class="metric-card">
+          <div class="metric-header">
+            <span class="metric-label">Users</span>
+            <span class="metric-change positive">+89%</span>
+          </div>
+          <div class="metric-value">48.2K</div>
+          <svg class="sparkline" viewBox="0 0 200 40">
+            <path class="sparkline-area" d="M0,30 L20,28 L40,35 L60,30 L80,25 L100,28 L120,20 L140,18 L160,15 L180,12 L200,8 L200,40 L0,40 Z"/>
+            <path class="sparkline-path" d="M0,30 L20,28 L40,35 L60,30 L80,25 L100,28 L120,20 L140,18 L160,15 L180,12 L200,8"/>
+          </svg>
+        </div>
+        <div class="metric-card">
+          <div class="metric-header">
+            <span class="metric-label">Engagement</span>
+            <span class="metric-change negative">-12%</span>
+          </div>
+          <div class="metric-value">94.2%</div>
+          <svg class="sparkline" viewBox="0 0 200 40">
+            <path class="sparkline-area" d="M0,10 L20,12 L40,8 L60,15 L80,12 L100,18 L120,15 L140,20 L160,18 L180,25 L200,22 L200,40 L0,40 Z"/>
+            <path class="sparkline-path" d="M0,10 L20,12 L40,8 L60,15 L80,12 L100,18 L120,15 L140,20 L160,18 L180,25 L200,22"/>
+          </svg>
+        </div>
+      </div>
+
+      <div class="cta-section">
+        <button class="btn-primary">Start Growing Now</button>
+      </div>
+
+      <div class="features">
+        <div class="feature">
+          <div class="feature-icon">📈</div>
+          <h3>Analytics</h3>
+          <p>Real-time insights</p>
+        </div>
+        <div class="feature">
+          <div class="feature-icon">🚀</div>
+          <h3>Growth</h3>
+          <p>Automated scaling</p>
+        </div>
+        <div class="feature">
+          <div class="feature-icon">💎</div>
+          <h3>Premium</h3>
+          <p>Enterprise ready</p>
+        </div>
+        <div class="feature">
+          <div class="feature-icon">⚡</div>
+          <h3>Speed</h3>
+          <p>Lightning fast</p>
+        </div>
+      </div>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/massive-icons.html 🔗

@@ -0,0 +1,199 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Massive Icons in Cards</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+  <script src="https://unpkg.com/lucide@latest"></script>
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: #f8fafc;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: #ffffff;
+      border-radius: 24px;
+      padding: 48px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      box-shadow: 0 4px 24px rgba(0,0,0,0.08);
+    }
+
+    /* Header */
+    .header {
+      text-align: center;
+      margin-bottom: 40px;
+    }
+
+    .header h1 {
+      font-size: 32px;
+      font-weight: 700;
+      color: #0f172a;
+      margin-bottom: 12px;
+    }
+
+    .header p {
+      font-size: 16px;
+      color: #64748b;
+    }
+
+    /* Features grid */
+    .features {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 24px;
+      flex: 1;
+    }
+
+    /* THE PROBLEM: Cards with massive left-aligned icons */
+    .feature-card {
+      background: #f8fafc;
+      border-radius: 16px;
+      padding: 28px;
+      display: flex;
+      flex-direction: column;
+      align-items: flex-start;
+    }
+
+    /* Oversized icon container */
+    .feature-icon {
+      width: 80px;
+      height: 80px;
+      background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
+      border-radius: 20px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin-bottom: 20px;
+    }
+
+    .feature-icon svg {
+      width: 40px;
+      height: 40px;
+      stroke: #3b82f6;
+      stroke-width: 1.5;
+    }
+
+    /* Different colored backgrounds */
+    .feature-card:nth-child(2) .feature-icon {
+      background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%);
+    }
+    .feature-card:nth-child(2) .feature-icon svg { stroke: #a855f7; }
+
+    .feature-card:nth-child(3) .feature-icon {
+      background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
+    }
+    .feature-card:nth-child(3) .feature-icon svg { stroke: #22c55e; }
+
+    .feature-card:nth-child(4) .feature-icon {
+      background: linear-gradient(135deg, #fff7ed 0%, #ffedd5 100%);
+    }
+    .feature-card:nth-child(4) .feature-icon svg { stroke: #f97316; }
+
+    .feature-card:nth-child(5) .feature-icon {
+      background: linear-gradient(135deg, #fdf2f8 0%, #fce7f3 100%);
+    }
+    .feature-card:nth-child(5) .feature-icon svg { stroke: #ec4899; }
+
+    .feature-card:nth-child(6) .feature-icon {
+      background: linear-gradient(135deg, #ecfeff 0%, #cffafe 100%);
+    }
+    .feature-card:nth-child(6) .feature-icon svg { stroke: #06b6d4; }
+
+    .feature-card h3 {
+      font-size: 18px;
+      font-weight: 700;
+      color: #0f172a;
+      margin-bottom: 8px;
+      text-align: left;
+    }
+
+    .feature-card p {
+      font-size: 14px;
+      color: #64748b;
+      line-height: 1.6;
+      text-align: left;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="header">
+      <h1>Why Choose Us</h1>
+      <p>Everything you need to build amazing products</p>
+    </div>
+
+    <div class="features">
+      <div class="feature-card">
+        <div class="feature-icon">
+          <i data-lucide="zap"></i>
+        </div>
+        <h3>Lightning Fast</h3>
+        <p>Built for speed and performance from the ground up.</p>
+      </div>
+
+      <div class="feature-card">
+        <div class="feature-icon">
+          <i data-lucide="shield"></i>
+        </div>
+        <h3>Secure</h3>
+        <p>Enterprise-grade security for your peace of mind.</p>
+      </div>
+
+      <div class="feature-card">
+        <div class="feature-icon">
+          <i data-lucide="refresh-cw"></i>
+        </div>
+        <h3>Automated</h3>
+        <p>Automate repetitive tasks and save time.</p>
+      </div>
+
+      <div class="feature-card">
+        <div class="feature-icon">
+          <i data-lucide="layers"></i>
+        </div>
+        <h3>Scalable</h3>
+        <p>Grows with your business needs seamlessly.</p>
+      </div>
+
+      <div class="feature-card">
+        <div class="feature-icon">
+          <i data-lucide="heart"></i>
+        </div>
+        <h3>User Friendly</h3>
+        <p>Intuitive interface that anyone can use.</p>
+      </div>
+
+      <div class="feature-card">
+        <div class="feature-icon">
+          <i data-lucide="bar-chart-3"></i>
+        </div>
+        <h3>Analytics</h3>
+        <p>Powerful insights to drive decisions.</p>
+      </div>
+    </div>
+
+  </div>
+
+  <script>
+    lucide.createIcons();
+  </script>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/modal-abuse.html 🔗

@@ -0,0 +1,436 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Modal Abuse</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: #1e1e1e;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: #f8fafc;
+      border-radius: 24px;
+      padding: 0;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      overflow: hidden;
+    }
+
+    /* Base app UI (dimmed) */
+    .app-ui {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      filter: brightness(0.4);
+    }
+
+    .app-header {
+      background: white;
+      padding: 16px 24px;
+      border-bottom: 1px solid #e5e7eb;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+
+    .app-logo {
+      font-weight: 700;
+      font-size: 18px;
+      color: #111;
+    }
+
+    .app-nav {
+      display: flex;
+      gap: 24px;
+    }
+
+    .app-nav a {
+      color: #6b7280;
+      text-decoration: none;
+      font-size: 14px;
+    }
+
+    .app-content {
+      flex: 1;
+      display: flex;
+    }
+
+    .app-sidebar {
+      width: 200px;
+      background: white;
+      border-right: 1px solid #e5e7eb;
+      padding: 16px;
+    }
+
+    .sidebar-item {
+      padding: 10px 12px;
+      border-radius: 6px;
+      font-size: 14px;
+      color: #6b7280;
+      margin-bottom: 4px;
+    }
+
+    .sidebar-item.active {
+      background: #f3f4f6;
+      color: #111;
+      font-weight: 500;
+    }
+
+    .app-main {
+      flex: 1;
+      padding: 24px;
+      background: #f9fafb;
+    }
+
+    .app-card {
+      background: white;
+      border-radius: 12px;
+      padding: 20px;
+      margin-bottom: 16px;
+    }
+
+    .app-card h3 {
+      font-size: 16px;
+      color: #111;
+      margin-bottom: 8px;
+    }
+
+    .app-card p {
+      font-size: 14px;
+      color: #6b7280;
+    }
+
+    /* Modal backdrop */
+    .modal-backdrop {
+      position: absolute;
+      inset: 0;
+      background: rgba(0, 0, 0, 0.5);
+    }
+
+    /* Large modal with advanced settings */
+    .modal {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      width: 600px;
+      max-height: 800px;
+      background: white;
+      border-radius: 16px;
+      box-shadow: 0 25px 50px rgba(0,0,0,0.3);
+      overflow: hidden;
+      z-index: 100;
+    }
+
+    .modal-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 20px 24px;
+      border-bottom: 1px solid #e5e7eb;
+    }
+
+    .modal-title {
+      font-size: 18px;
+      font-weight: 700;
+      color: #111;
+    }
+
+    .modal-close {
+      width: 32px;
+      height: 32px;
+      border-radius: 8px;
+      border: none;
+      background: #f3f4f6;
+      color: #6b7280;
+      font-size: 18px;
+      cursor: pointer;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .modal-body {
+      padding: 24px;
+      max-height: 600px;
+      overflow-y: auto;
+    }
+
+    /* Complex settings form that should be on a dedicated page */
+    .settings-section {
+      margin-bottom: 28px;
+    }
+
+    .settings-section h4 {
+      font-size: 14px;
+      font-weight: 600;
+      color: #374151;
+      margin-bottom: 16px;
+      padding-bottom: 8px;
+      border-bottom: 1px solid #e5e7eb;
+    }
+
+    .setting-row {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 12px 0;
+      border-bottom: 1px solid #f3f4f6;
+    }
+
+    .setting-row:last-child {
+      border-bottom: none;
+    }
+
+    .setting-label {
+      font-size: 14px;
+      color: #374151;
+    }
+
+    .setting-desc {
+      font-size: 12px;
+      color: #9ca3af;
+      margin-top: 2px;
+    }
+
+    .toggle {
+      width: 44px;
+      height: 24px;
+      background: #e5e7eb;
+      border-radius: 12px;
+      position: relative;
+    }
+
+    .toggle.on {
+      background: #3b82f6;
+    }
+
+    .toggle::after {
+      content: '';
+      position: absolute;
+      width: 20px;
+      height: 20px;
+      background: white;
+      border-radius: 50%;
+      top: 2px;
+      left: 2px;
+      transition: 0.2s;
+    }
+
+    .toggle.on::after {
+      left: 22px;
+    }
+
+    .select-input {
+      padding: 8px 12px;
+      border: 1px solid #d1d5db;
+      border-radius: 6px;
+      font-size: 13px;
+      color: #374151;
+      background: white;
+    }
+
+    .text-input {
+      padding: 8px 12px;
+      border: 1px solid #d1d5db;
+      border-radius: 6px;
+      font-size: 13px;
+      width: 180px;
+    }
+
+    .modal-footer {
+      display: flex;
+      gap: 12px;
+      justify-content: flex-end;
+      padding: 16px 24px;
+      border-top: 1px solid #e5e7eb;
+      background: #f9fafb;
+    }
+
+    .btn {
+      padding: 10px 20px;
+      border-radius: 8px;
+      font-size: 14px;
+      font-weight: 600;
+      cursor: pointer;
+      border: none;
+    }
+
+    .btn-secondary {
+      background: white;
+      border: 1px solid #d1d5db;
+      color: #374151;
+    }
+
+    .btn-primary {
+      background: #2563eb;
+      color: white;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <!-- Base app -->
+    <div class="app-ui">
+      <div class="app-header">
+        <div class="app-logo">Dashboard</div>
+        <div class="app-nav">
+          <a href="#">Home</a>
+          <a href="#">Projects</a>
+          <a href="#">Team</a>
+        </div>
+      </div>
+      <div class="app-content">
+        <div class="app-sidebar">
+          <div class="sidebar-item active">Overview</div>
+          <div class="sidebar-item">Analytics</div>
+          <div class="sidebar-item">Reports</div>
+          <div class="sidebar-item">Settings</div>
+        </div>
+        <div class="app-main">
+          <div class="app-card">
+            <h3>Recent Activity</h3>
+            <p>Your recent actions will appear here...</p>
+          </div>
+          <div class="app-card">
+            <h3>Quick Stats</h3>
+            <p>Overview of your account metrics...</p>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="modal-backdrop"></div>
+
+    <!-- Advanced settings crammed into a modal instead of a proper settings page -->
+    <div class="modal">
+      <div class="modal-header">
+        <span class="modal-title">Advanced Settings</span>
+        <button class="modal-close">×</button>
+      </div>
+      <div class="modal-body">
+        <div class="settings-section">
+          <h4>Notifications</h4>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">Email notifications</div>
+              <div class="setting-desc">Receive updates via email</div>
+            </div>
+            <div class="toggle on"></div>
+          </div>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">Push notifications</div>
+              <div class="setting-desc">Browser push alerts</div>
+            </div>
+            <div class="toggle"></div>
+          </div>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">Notification frequency</div>
+            </div>
+            <select class="select-input">
+              <option>Instant</option>
+              <option>Daily digest</option>
+              <option>Weekly</option>
+            </select>
+          </div>
+        </div>
+
+        <div class="settings-section">
+          <h4>Privacy & Security</h4>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">Two-factor authentication</div>
+              <div class="setting-desc">Add extra security</div>
+            </div>
+            <div class="toggle on"></div>
+          </div>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">Session timeout</div>
+            </div>
+            <select class="select-input">
+              <option>30 minutes</option>
+              <option>1 hour</option>
+              <option>4 hours</option>
+            </select>
+          </div>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">Activity logging</div>
+              <div class="setting-desc">Track account activity</div>
+            </div>
+            <div class="toggle on"></div>
+          </div>
+        </div>
+
+        <div class="settings-section">
+          <h4>Data & Export</h4>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">Auto-backup</div>
+              <div class="setting-desc">Automatic data backups</div>
+            </div>
+            <div class="toggle"></div>
+          </div>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">Export format</div>
+            </div>
+            <select class="select-input">
+              <option>CSV</option>
+              <option>JSON</option>
+              <option>Excel</option>
+            </select>
+          </div>
+        </div>
+
+        <div class="settings-section">
+          <h4>API Settings</h4>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">API Key</div>
+            </div>
+            <input type="text" class="text-input" value="sk-xxxxx-xxxxx" readonly>
+          </div>
+          <div class="setting-row">
+            <div>
+              <div class="setting-label">Rate limit</div>
+            </div>
+            <select class="select-input">
+              <option>100/min</option>
+              <option>500/min</option>
+              <option>1000/min</option>
+            </select>
+          </div>
+        </div>
+      </div>
+      <div class="modal-footer">
+        <button class="btn btn-secondary">Cancel</button>
+        <button class="btn btn-primary">Save Changes</button>
+      </div>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/purple-gradients.html 🔗

@@ -0,0 +1,241 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Purple Gradients Everywhere</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: linear-gradient(135deg, #1a0a2e 0%, #2d1b4e 50%, #1a0a2e 100%);
+      color: white;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: linear-gradient(145deg, #2a1a4a 0%, #1a0a2e 100%);
+      border-radius: 32px;
+      padding: 48px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      overflow: hidden;
+    }
+
+    /* Purple gradient orbs everywhere */
+    .orb {
+      position: absolute;
+      border-radius: 50%;
+      filter: blur(80px);
+      opacity: 0.6;
+    }
+    .orb-1 { width: 400px; height: 400px; background: linear-gradient(135deg, #8b5cf6, #a855f7); top: -100px; right: -100px; }
+    .orb-2 { width: 300px; height: 300px; background: linear-gradient(135deg, #7c3aed, #c084fc); bottom: -50px; left: -50px; }
+    .orb-3 { width: 200px; height: 200px; background: linear-gradient(135deg, #a855f7, #d946ef); top: 40%; left: 30%; }
+
+    .content {
+      position: relative;
+      z-index: 1;
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+    }
+
+    /* Navigation */
+    nav {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 60px;
+    }
+
+    .logo {
+      font-size: 24px;
+      font-weight: 800;
+      background: linear-gradient(135deg, #a855f7, #ec4899);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
+
+    .nav-links {
+      display: flex;
+      gap: 32px;
+    }
+
+    .nav-links a {
+      color: rgba(255,255,255,0.7);
+      text-decoration: none;
+      font-size: 14px;
+    }
+
+    .nav-cta {
+      background: linear-gradient(135deg, #8b5cf6, #a855f7) !important;
+      color: white !important;
+      padding: 10px 20px;
+      border-radius: 999px;
+      font-weight: 600;
+    }
+
+    /* Hero */
+    .hero {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      text-align: center;
+      padding: 0 60px;
+    }
+
+    .badge {
+      display: inline-flex;
+      align-items: center;
+      gap: 8px;
+      background: linear-gradient(135deg, rgba(139, 92, 246, 0.3), rgba(168, 85, 247, 0.2));
+      border: 1px solid rgba(168, 85, 247, 0.4);
+      padding: 8px 16px;
+      border-radius: 999px;
+      font-size: 13px;
+      margin: 0 auto 24px;
+      color: #c4b5fd;
+    }
+
+    .badge::before {
+      content: '✨';
+    }
+
+    h1 {
+      font-size: 64px;
+      font-weight: 800;
+      line-height: 1.1;
+      margin-bottom: 24px;
+      background: linear-gradient(135deg, #ffffff 0%, #c4b5fd 50%, #a855f7 100%);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
+
+    .subtitle {
+      font-size: 20px;
+      color: rgba(255,255,255,0.6);
+      max-width: 500px;
+      margin: 0 auto 40px;
+      line-height: 1.6;
+    }
+
+    .cta-group {
+      display: flex;
+      gap: 16px;
+      justify-content: center;
+    }
+
+    .btn-primary {
+      background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 50%, #c026d3 100%);
+      color: white;
+      padding: 16px 32px;
+      border-radius: 12px;
+      font-weight: 600;
+      font-size: 16px;
+      border: none;
+      cursor: pointer;
+      box-shadow: 0 0 40px rgba(139, 92, 246, 0.5);
+    }
+
+    .btn-secondary {
+      background: linear-gradient(135deg, rgba(139, 92, 246, 0.2), rgba(168, 85, 247, 0.1));
+      border: 1px solid rgba(168, 85, 247, 0.5);
+      color: #c4b5fd;
+      padding: 16px 32px;
+      border-radius: 12px;
+      font-weight: 600;
+      font-size: 16px;
+      cursor: pointer;
+    }
+
+    /* Stats */
+    .stats {
+      display: flex;
+      justify-content: center;
+      gap: 60px;
+      margin-top: 60px;
+      padding-top: 40px;
+      border-top: 1px solid rgba(139, 92, 246, 0.3);
+    }
+
+    .stat {
+      text-align: center;
+    }
+
+    .stat-value {
+      font-size: 36px;
+      font-weight: 800;
+      background: linear-gradient(135deg, #a855f7, #ec4899);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
+
+    .stat-label {
+      font-size: 14px;
+      color: rgba(255,255,255,0.5);
+      margin-top: 4px;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="orb orb-1"></div>
+    <div class="orb orb-2"></div>
+    <div class="orb orb-3"></div>
+
+    <div class="content">
+      <nav>
+        <div class="logo">Synthwave.ai</div>
+        <div class="nav-links">
+          <a href="#">Features</a>
+          <a href="#">Pricing</a>
+          <a href="#">About</a>
+          <a href="#" class="nav-cta">Get Started</a>
+        </div>
+      </nav>
+
+      <div class="hero">
+        <span class="badge">New: AI-Powered Everything</span>
+        <h1>Build the Future<br>With AI Magic</h1>
+        <p class="subtitle">Transform your workflow with our revolutionary AI platform. Purple gradients included.</p>
+        <div class="cta-group">
+          <button class="btn-primary">Start Free Trial</button>
+          <button class="btn-secondary">Watch Demo</button>
+        </div>
+
+        <div class="stats">
+          <div class="stat">
+            <div class="stat-value">10K+</div>
+            <div class="stat-label">Active Users</div>
+          </div>
+          <div class="stat">
+            <div class="stat-value">99.9%</div>
+            <div class="stat-label">Uptime</div>
+          </div>
+          <div class="stat">
+            <div class="stat-value">50M+</div>
+            <div class="stat-label">API Calls</div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/redundant-ux-writing.html 🔗

@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Redundant UX Writing</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: #f0f4f8;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: #ffffff;
+      border-radius: 24px;
+      padding: 48px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      box-shadow: 0 4px 24px rgba(0,0,0,0.08);
+    }
+
+    /* Page Header */
+    .page-header {
+      text-align: center;
+      margin-bottom: 32px;
+      padding-bottom: 24px;
+      border-bottom: 1px solid #e5e7eb;
+    }
+
+    .page-header h1 {
+      font-size: 28px;
+      font-weight: 700;
+      color: #1f2937;
+      margin-bottom: 8px;
+    }
+
+    .page-header p {
+      font-size: 14px;
+      color: #6b7280;
+    }
+
+    /* Section with DUPLICATE heading */
+    .section {
+      background: #f9fafb;
+      border-radius: 16px;
+      padding: 28px;
+      margin-bottom: 24px;
+    }
+
+    .section-header {
+      margin-bottom: 20px;
+      padding-bottom: 16px;
+      border-bottom: 1px solid #e5e7eb;
+    }
+
+    .section-header h2 {
+      font-size: 20px;
+      font-weight: 700;
+      color: #1f2937;
+      margin-bottom: 4px;
+    }
+
+    .section-header p {
+      font-size: 13px;
+      color: #6b7280;
+    }
+
+    /* Form */
+    .form-group {
+      margin-bottom: 20px;
+    }
+
+    .form-label {
+      display: block;
+      font-size: 14px;
+      font-weight: 600;
+      color: #374151;
+      margin-bottom: 6px;
+    }
+
+    .form-sublabel {
+      font-size: 12px;
+      color: #9ca3af;
+      margin-bottom: 8px;
+      display: block;
+    }
+
+    .form-input {
+      width: 100%;
+      padding: 12px 16px;
+      border: 1px solid #d1d5db;
+      border-radius: 8px;
+      font-size: 14px;
+      font-family: inherit;
+    }
+
+    .form-hint {
+      font-size: 11px;
+      color: #6b7280;
+      margin-top: 6px;
+      line-height: 1.4;
+    }
+
+    /* Button section */
+    .button-section {
+      margin-top: 24px;
+      padding-top: 20px;
+      border-top: 1px solid #e5e7eb;
+    }
+
+    .btn-submit {
+      width: 100%;
+      background: #2563eb;
+      color: white;
+      padding: 14px 24px;
+      border-radius: 8px;
+      font-weight: 600;
+      font-size: 15px;
+      border: none;
+      cursor: pointer;
+      margin-bottom: 12px;
+    }
+
+    .btn-helper {
+      font-size: 12px;
+      color: #6b7280;
+      text-align: center;
+      line-height: 1.5;
+    }
+
+    /* Form row */
+    .form-row {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 16px;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <!-- PAGE HEADER -->
+    <div class="page-header">
+      <h1>Create Your Account</h1>
+      <p>Sign up to get started with our platform</p>
+    </div>
+
+    <!-- SECTION WITH DUPLICATE HEADING -->
+    <div class="section">
+      <div class="section-header">
+        <h2>Create Your Account</h2>
+        <p>Fill out the form below to create your new account</p>
+      </div>
+
+      <div class="form-row">
+        <div class="form-group">
+          <label class="form-label">First Name</label>
+          <span class="form-sublabel">Enter your first name</span>
+          <input type="text" class="form-input" placeholder="Enter first name">
+          <span class="form-hint">Your first name as it appears on documents</span>
+        </div>
+        <div class="form-group">
+          <label class="form-label">Last Name</label>
+          <span class="form-sublabel">Enter your last name</span>
+          <input type="text" class="form-input" placeholder="Enter last name">
+          <span class="form-hint">Your last name as it appears on documents</span>
+        </div>
+      </div>
+
+      <div class="form-group">
+        <label class="form-label">Email Address</label>
+        <span class="form-sublabel">Provide a valid email address</span>
+        <input type="email" class="form-input" placeholder="example@email.com">
+        <span class="form-hint">Your email will be used to send you important notifications via email</span>
+      </div>
+
+      <div class="form-group">
+        <label class="form-label">Create Password</label>
+        <span class="form-sublabel">Choose a secure password</span>
+        <input type="password" class="form-input" placeholder="Enter password">
+        <span class="form-hint">Password must be at least 8 characters for security</span>
+      </div>
+
+      <div class="button-section">
+        <button class="btn-submit">Create Account</button>
+        <p class="btn-helper">Click the button above to create your account and complete registration</p>
+      </div>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/thick-border-cards.html 🔗

@@ -0,0 +1,205 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Pattern: Left Border Accent Cards</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: #f1f5f9;
+      padding: 40px;
+    }
+
+    .container {
+      max-width: 960px;
+      width: 100%;
+      margin: 0 auto;
+      background: #ffffff;
+      border-radius: 24px;
+      padding: 48px;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+      box-shadow: 0 4px 24px rgba(0,0,0,0.08);
+    }
+
+    /* Header */
+    .header {
+      text-align: center;
+      margin-bottom: 40px;
+    }
+
+    .header h1 {
+      font-size: 32px;
+      font-weight: 700;
+      color: #0f172a;
+      margin-bottom: 12px;
+    }
+
+    .header p {
+      font-size: 16px;
+      color: #64748b;
+    }
+
+    /* Cards grid */
+    .cards {
+      display: grid;
+      grid-template-columns: repeat(2, 1fr);
+      gap: 24px;
+      flex: 1;
+    }
+
+    /* THE PROBLEM: Left border accent on rounded cards */
+    .card {
+      background: #ffffff;
+      border-radius: 16px;
+      padding: 28px;
+      display: flex;
+      flex-direction: column;
+      box-shadow: 0 2px 8px rgba(0,0,0,0.06);
+      /* The weird left border that doesn't match the rounded corners */
+      border-left: 4px solid;
+    }
+
+    .card-blue { border-left-color: #3b82f6; }
+    .card-purple { border-left-color: #a855f7; }
+    .card-green { border-left-color: #22c55e; }
+    .card-orange { border-left-color: #f97316; }
+
+    .card-icon {
+      font-size: 28px;
+      margin-bottom: 16px;
+    }
+
+    .card h3 {
+      font-size: 18px;
+      font-weight: 700;
+      color: #0f172a;
+      margin-bottom: 10px;
+    }
+
+    .card p {
+      font-size: 14px;
+      color: #64748b;
+      line-height: 1.6;
+      margin-bottom: 20px;
+      flex: 1;
+    }
+
+    .card-btn {
+      padding: 10px 18px;
+      border-radius: 8px;
+      font-size: 14px;
+      font-weight: 600;
+      cursor: pointer;
+      border: none;
+      width: fit-content;
+      background: #f1f5f9;
+      color: #475569;
+    }
+
+    /* Stats bar - also with left border accent */
+    .stats-bar {
+      display: flex;
+      gap: 16px;
+      margin-top: 24px;
+    }
+
+    .stat-item {
+      flex: 1;
+      padding: 20px;
+      border-radius: 12px;
+      background: #f8fafc;
+      border-left: 4px solid;
+    }
+
+    .stat-item:nth-child(1) { border-left-color: #06b6d4; }
+    .stat-item:nth-child(2) { border-left-color: #ec4899; }
+    .stat-item:nth-child(3) { border-left-color: #eab308; }
+    .stat-item:nth-child(4) { border-left-color: #6366f1; }
+
+    .stat-value {
+      font-size: 28px;
+      font-weight: 800;
+      color: #0f172a;
+      margin-bottom: 4px;
+    }
+
+    .stat-label {
+      font-size: 12px;
+      color: #64748b;
+      font-weight: 500;
+    }
+
+
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="header">
+      <h1>Our Services</h1>
+      <p>Discover what we can do for your business</p>
+    </div>
+
+    <div class="cards">
+      <div class="card card-blue">
+        <div class="card-icon">🚀</div>
+        <h3>Growth Strategy</h3>
+        <p>Accelerate your business growth with our proven strategies and expert guidance.</p>
+        <button class="card-btn">Learn More</button>
+      </div>
+
+      <div class="card card-purple">
+        <div class="card-icon">💡</div>
+        <h3>Innovation Lab</h3>
+        <p>Transform ideas into reality with our cutting-edge innovation services.</p>
+        <button class="card-btn">Explore</button>
+      </div>
+
+      <div class="card card-green">
+        <div class="card-icon">📊</div>
+        <h3>Data Analytics</h3>
+        <p>Make data-driven decisions with our comprehensive analytics solutions.</p>
+        <button class="card-btn">Get Started</button>
+      </div>
+
+      <div class="card card-orange">
+        <div class="card-icon">🎯</div>
+        <h3>Marketing</h3>
+        <p>Reach your target audience effectively with our marketing expertise.</p>
+        <button class="card-btn">Contact Us</button>
+      </div>
+    </div>
+
+    <div class="stats-bar">
+      <div class="stat-item">
+        <div class="stat-value">500+</div>
+        <div class="stat-label">Clients</div>
+      </div>
+      <div class="stat-item">
+        <div class="stat-value">98%</div>
+        <div class="stat-label">Satisfaction</div>
+      </div>
+      <div class="stat-item">
+        <div class="stat-value">24/7</div>
+        <div class="stat-label">Support</div>
+      </div>
+      <div class="stat-item">
+        <div class="stat-value">50+</div>
+        <div class="stat-label">Countries</div>
+      </div>
+    </div>
+
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/antipattern-examples/visual-mode-demo.html 🔗

@@ -0,0 +1,231 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Visual Mode Demo</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
+  <style>
+    * { margin: 0; padding: 0; box-sizing: border-box; }
+
+    body {
+      font-family: 'Inter', sans-serif;
+      min-height: 100vh;
+      background: linear-gradient(135deg, #1a0a2e 0%, #2d1b4e 50%, #1a0a2e 100%);
+      color: white;
+      padding: 60px 24px 24px;
+      overflow-x: hidden;
+    }
+
+    .container {
+      max-width: 720px;
+      margin: 0 auto;
+      background: linear-gradient(145deg, #2a1a4a 0%, #1a0a2e 100%);
+      border-radius: 24px;
+      padding: 32px;
+      position: relative;
+      overflow: hidden;
+    }
+
+    /* orbs removed to reduce detector noise */
+
+    .content { position: relative; z-index: 1; }
+
+    nav {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 32px;
+    }
+
+    .logo {
+      font-size: 18px;
+      font-weight: 800;
+      background: linear-gradient(135deg, #a855f7, #ec4899);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
+
+    .nav-links {
+      display: flex;
+      gap: 20px;
+      align-items: center;
+    }
+
+    .nav-links a {
+      color: rgba(255,255,255,0.6);
+      text-decoration: none;
+      font-size: 13px;
+    }
+
+    .nav-cta {
+      background: linear-gradient(135deg, #8b5cf6, #a855f7) !important;
+      color: white !important;
+      padding: 8px 16px;
+      border-radius: 999px;
+      font-weight: 600;
+      font-size: 12px;
+    }
+
+    .hero {
+      text-align: center;
+      padding: 0 20px;
+      margin-bottom: 32px;
+    }
+
+    .badge {
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+      background: linear-gradient(135deg, rgba(139,92,246,0.3), rgba(168,85,247,0.2));
+      border: 1px solid rgba(168,85,247,0.4);
+      padding: 6px 14px;
+      border-radius: 999px;
+      font-size: 11px;
+      margin-bottom: 16px;
+      color: #c4b5fd;
+    }
+
+    .badge::before { content: '\2728'; }
+
+    h1 {
+      font-size: 36px;
+      font-weight: 800;
+      line-height: 1.1;
+      margin-bottom: 12px;
+      background: linear-gradient(135deg, #fff 0%, #c4b5fd 50%, #a855f7 100%);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
+
+    .subtitle {
+      font-size: 14px;
+      color: rgba(255,255,255,0.5);
+      max-width: 400px;
+      margin: 0 auto 24px;
+      line-height: 1.6;
+    }
+
+    /* Side-tab border cards */
+    .cards {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 12px;
+      margin-bottom: 24px;
+    }
+
+    .card {
+      background: rgba(255,255,255,0.06);
+      border-radius: 12px;
+      padding: 20px 16px;
+      backdrop-filter: blur(8px);
+    }
+
+    .card-heading {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+      margin-bottom: 8px;
+    }
+
+    .card-icon {
+      font-size: 18px;
+      line-height: 1;
+    }
+
+    .card h3 {
+      font-size: 14px;
+      font-weight: 700;
+    }
+
+    .card p {
+      font-size: 13px;
+      color: rgba(255,255,255,0.55);
+      line-height: 1.5;
+    }
+
+    /* Stats */
+    .stats {
+      display: flex;
+      justify-content: center;
+      gap: 40px;
+      padding-top: 20px;
+      border-top: 1px solid rgba(139,92,246,0.3);
+    }
+
+    .stat-value {
+      font-size: 24px;
+      font-weight: 800;
+      background: linear-gradient(135deg, #a855f7, #ec4899);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
+
+    .stat-label {
+      font-size: 11px;
+      color: rgba(255,255,255,0.4);
+      margin-top: 2px;
+    }
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="content">
+      <nav>
+        <div class="logo">Synthwave.ai</div>
+        <div class="nav-links">
+          <a href="#">Features</a>
+          <a href="#">Pricing</a>
+          <a href="#" class="nav-cta">Get Started</a>
+        </div>
+      </nav>
+
+      <div class="hero">
+        <span class="badge">New: AI-Powered Everything</span>
+        <h1>Build the Future<br>With AI Magic</h1>
+        <p class="subtitle">Transform your workflow with our revolutionary AI platform. Purple gradients included.</p>
+      </div>
+
+      <div class="cards">
+        <div class="card">
+          <div class="card-heading">
+            <span class="card-icon">&#9889;</span>
+            <h3>Fast Performance</h3>
+          </div>
+          <p>Blazing fast inference with our optimized pipeline.</p>
+        </div>
+        <div class="card">
+          <div class="card-heading">
+            <span class="card-icon">&#128274;</span>
+            <h3>Secure & Safe</h3>
+          </div>
+          <p>Enterprise-grade security with encrypted data.</p>
+        </div>
+        <div class="card">
+          <div class="card-heading">
+            <span class="card-icon">&#128200;</span>
+            <h3>Analytics</h3>
+          </div>
+          <p>Powerful insights to help you make better decisions.</p>
+        </div>
+      </div>
+
+      <div class="stats">
+        <div class="stat">
+          <div class="stat-value">10K+</div>
+          <div class="stat-label">Active Users</div>
+        </div>
+        <div class="stat">
+          <div class="stat-value">99.9%</div>
+          <div class="stat-label">Uptime Guaranteed</div>
+        </div>
+        <div class="stat">
+          <div class="stat-value">50M+</div>
+          <div class="stat-label">Data Processed</div>
+        </div>
+      </div>
+    </div>
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

public/app.js 🔗

@@ -0,0 +1,226 @@
+import {
+	initGlassTerminal,
+	renderTerminalLayout,
+} from "./js/components/glass-terminal.js";
+import { initLensEffect } from "./js/components/lens.js";
+import { initFrameworkViz } from "./js/components/framework-viz.js";
+import { initScrollReveal } from "./js/utils/reveal.js";
+import { initAnchorScroll, initHashTracking } from "./js/utils/scroll.js";
+import { initSectionNav } from "./js/components/section-nav.js";
+import { initFoundationGrid } from "./js/components/foundation-grid.js";
+
+// ============================================
+// STATE
+// ============================================
+
+let allCommands = [];
+
+// ============================================
+// CONTENT LOADING
+// ============================================
+
+function escapeHtml(value) {
+	if (typeof value !== "string") return "";
+	return value
+		.replaceAll("&", "&amp;")
+		.replaceAll("<", "&lt;")
+		.replaceAll(">", "&gt;")
+		.replaceAll('"', "&quot;")
+		.replaceAll("'", "&#39;");
+}
+
+async function loadContent() {
+	try {
+		const [commandsRes, patternsRes] = await Promise.all([
+			fetch("/api/commands"),
+			fetch("/api/patterns"),
+		]);
+
+		// Check for HTTP errors
+		if (!commandsRes.ok) {
+			throw new Error(`Commands API failed: ${commandsRes.status}`);
+		}
+		if (!patternsRes.ok) {
+			throw new Error(`Patterns API failed: ${patternsRes.status}`);
+		}
+
+		allCommands = await commandsRes.json();
+		const patternsData = await patternsRes.json();
+
+		// Render commands (Glass Terminal)
+		renderTerminalLayout(allCommands);
+
+		// Initialize gallery card stack
+		initGalleryStack();
+
+		// Render patterns with tabbed navigation
+		renderPatternsWithTabs(patternsData.patterns, patternsData.antipatterns);
+	} catch (error) {
+		console.error("Failed to load content:", error);
+		showLoadError(error);
+	}
+}
+
+function showLoadError(error) {
+	// Show error in commands section
+	const commandsGallery = document.querySelector('.commands-gallery');
+	if (commandsGallery) {
+		commandsGallery.innerHTML = `
+			<div class="load-error" role="alert">
+				<div class="load-error-icon" aria-hidden="true">⚠</div>
+				<h3 class="load-error-title">Failed to load commands</h3>
+				<p class="load-error-text">There was a problem loading the content. Please check your connection and try again.</p>
+				<button class="btn btn-secondary load-error-retry" onclick="location.reload()">
+					Retry
+				</button>
+			</div>
+		`;
+	}
+
+	// Show error in patterns section
+	const patternsContainer = document.getElementById("patterns-categories");
+	if (patternsContainer) {
+		patternsContainer.innerHTML = `
+			<div class="load-error" role="alert">
+				<div class="load-error-icon" aria-hidden="true">⚠</div>
+				<h3 class="load-error-title">Failed to load patterns</h3>
+				<p class="load-error-text">There was a problem loading the content. Please check your connection and try again.</p>
+				<button class="btn btn-secondary load-error-retry" onclick="location.reload()">
+					Retry
+				</button>
+			</div>
+		`;
+	}
+}
+
+function initGalleryStack() {
+	const container = document.querySelector('.gallery-stack-container');
+	const stack = document.getElementById('gallery-stack');
+	if (!stack || !container) return;
+
+	const cards = stack.querySelectorAll('.gallery-stack-card');
+	const counter = container.querySelector('.gallery-stack-counter');
+	const total = cards.length;
+	let current = 0;
+	let lastScroll = 0;
+
+	function update() {
+		cards.forEach((card, i) => {
+			const offset = (i - current + total) % total;
+			card.dataset.offset = offset;
+		});
+	}
+
+	function next() { current = (current + 1) % total; update(); }
+	function prev() { current = (current - 1 + total) % total; update(); }
+
+	container.querySelector('.gallery-stack-prev').addEventListener('click', prev);
+	container.querySelector('.gallery-stack-next').addEventListener('click', next);
+
+	stack.addEventListener('wheel', (e) => {
+		e.preventDefault();
+		const now = Date.now();
+		if (now - lastScroll < 350) return;
+		lastScroll = now;
+		if (e.deltaY > 0) next(); else prev();
+	}, { passive: false });
+
+	update();
+}
+
+function renderPatternsWithTabs(patterns, antipatterns) {
+	const container = document.getElementById("patterns-categories");
+	if (!container || !patterns || !antipatterns) return;
+
+	const antipatternMap = {};
+	antipatterns.forEach(cat => { antipatternMap[cat.name] = cat.items; });
+
+	const tabsHTML = patterns.map((cat, i) =>
+		`<button class="patterns-tab${i === 0 ? ' is-active' : ''}" data-index="${i}">${escapeHtml(cat.name)}</button>`
+	).join('');
+
+	const panelsHTML = patterns.map((cat, i) => {
+		const antiItems = antipatternMap[cat.name] || [];
+		return `
+		<div class="patterns-content${i === 0 ? ' is-active' : ''}" data-index="${i}">
+			<div class="patterns-col patterns-col--dont">
+				<ul>${antiItems.map(item => `<li>${escapeHtml(item)}</li>`).join('')}</ul>
+			</div>
+			<div class="patterns-col patterns-col--do">
+				<ul>${cat.items.map(item => `<li>${escapeHtml(item)}</li>`).join('')}</ul>
+			</div>
+		</div>`;
+	}).join('');
+
+	container.innerHTML = `<div class="patterns-tabs">${tabsHTML}</div>${panelsHTML}`;
+
+	container.addEventListener('click', (e) => {
+		const tab = e.target.closest('.patterns-tab');
+		if (!tab) return;
+		const index = tab.dataset.index;
+		container.querySelectorAll('.patterns-tab').forEach(t => t.classList.remove('is-active'));
+		container.querySelectorAll('.patterns-content').forEach(p => p.classList.remove('is-active'));
+		tab.classList.add('is-active');
+		container.querySelector(`.patterns-content[data-index="${index}"]`).classList.add('is-active');
+	});
+}
+
+// ============================================
+// EVENT HANDLERS
+// ============================================
+
+// Handle bundle download clicks via event delegation.
+// Each download button carries the full bundle name in data-bundle (e.g.
+// "universal" or "universal-prefixed") so the handler is just a redirect.
+document.addEventListener("click", (e) => {
+	const bundleBtn = e.target.closest("[data-bundle]");
+	if (bundleBtn) {
+		const bundleName = bundleBtn.dataset.bundle;
+		window.location.href = `/api/download/bundle/${bundleName}`;
+	}
+
+	// Handle copy button clicks
+	const copyBtn = e.target.closest("[data-copy]");
+	if (copyBtn) {
+		const textToCopy = copyBtn.dataset.copy;
+		const onCopied = () => {
+			copyBtn.classList.add('copied');
+			setTimeout(() => copyBtn.classList.remove('copied'), 1500);
+		};
+		if (navigator.clipboard?.writeText) {
+			navigator.clipboard.writeText(textToCopy).then(onCopied).catch(() => {});
+		} else {
+			// Fallback for non-HTTPS or older browsers
+			const ta = Object.assign(document.createElement('textarea'), { value: textToCopy, style: 'position:fixed;left:-9999px' });
+			document.body.appendChild(ta);
+			ta.select();
+			try { document.execCommand('copy'); onCopied(); } catch {}
+			ta.remove();
+		}
+	}
+});
+
+
+// ============================================
+// STARTUP
+// ============================================
+
+function init() {
+	initAnchorScroll();
+	initHashTracking();
+	initLensEffect();
+	initScrollReveal();
+	initGlassTerminal();
+	initFrameworkViz();
+	initFoundationGrid();
+	initSectionNav();
+	loadContent();
+
+	document.body.classList.add("loaded");
+}
+
+if (document.readyState === "loading") {
+	document.addEventListener("DOMContentLoaded", init);
+} else {
+	init();
+}

public/assets/diamond.svg 🔗

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" width="1000" height="1000">
+  <polygon points="500,126 874,500 500,874 126,500" fill="#000"/>
+</svg>

public/assets/newsletter-icon.svg 🔗

@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
+  <defs>
+    <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
+      <stop offset="0%" stop-color="#1f1f1f"/>
+      <stop offset="100%" stop-color="#141414"/>
+    </linearGradient>
+    <linearGradient id="accent" x1="0" y1="0" x2="0" y2="1">
+      <stop offset="0%" stop-color="#d4456a"/>
+      <stop offset="100%" stop-color="#b03058"/>
+    </linearGradient>
+  </defs>
+  <!-- Background -->
+  <rect width="256" height="256" rx="48" fill="url(#bg)"/>
+  <!-- Subtle accent line -->
+  <rect x="56" y="196" width="144" height="2.5" rx="1.25" fill="url(#accent)" opacity="0.6"/>
+  <!-- Slash mark -->
+  <text x="128" y="178" font-family="system-ui, -apple-system, 'Helvetica Neue', sans-serif" font-size="160" font-weight="400" fill="#f5f3ef" text-anchor="middle" letter-spacing="-8">/</text>
+</svg>

public/assets/pi-logo.svg 🔗

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800">
+  <!-- P shape: outer boundary clockwise, inner hole counter-clockwise -->
+  <path fill="#fff" fill-rule="evenodd" d="
+    M165.29 165.29
+    H517.36
+    V400
+    H400
+    V517.36
+    H282.65
+    V634.72
+    H165.29
+    Z
+    M282.65 282.65
+    V400
+    H400
+    V282.65
+    Z
+  "/>
+  <!-- i dot -->
+  <path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/>
+</svg>

public/cheatsheet.html 🔗

@@ -0,0 +1,316 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <!-- Google tag (gtag.js) -->
+  <script async src="https://www.googletagmanager.com/gtag/js?id=G-TEXGHC7V34"></script>
+  <script>
+    window.dataLayer = window.dataLayer || [];
+    function gtag(){dataLayer.push(arguments);}
+    gtag('js', new Date());
+    gtag('config', 'G-TEXGHC7V34');
+  </script>
+
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Impeccable Command Cheatsheet</title>
+  <meta name="description" content="Quick reference for all 18 Impeccable commands. Print-friendly cheatsheet for design fluency in AI harnesses.">
+  <link rel="icon" type="image/svg+xml" href="./favicon.svg">
+  <link rel="preconnect" href="https://fonts.googleapis.com">
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+  <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Instrument+Sans:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
+  <link rel="stylesheet" href="./css/sub-pages.css">
+  <style>
+    :root {
+      --font-body: 'Instrument Sans', system-ui, sans-serif;
+      --font-mono: 'Space Grotesk', monospace;
+      --color-ink: #1a1a1a;
+      --color-paper: #fafafa;
+      --color-mist: #e5e5e5;
+      --color-ash: #737373;
+      --color-accent: #d946ef;
+    }
+
+    @media (prefers-color-scheme: dark) {
+      :root {
+        --color-ink: #fafafa;
+        --color-paper: #1a1a1a;
+        --color-mist: #333;
+        --color-ash: #a3a3a3;
+      }
+    }
+
+    body {
+      font-family: var(--font-body);
+      background: var(--color-paper);
+      color: var(--color-ink);
+      line-height: 1.5;
+    }
+
+    .cheatsheet-page-header,
+    main,
+    footer {
+      max-width: 900px;
+      margin-left: auto;
+      margin-right: auto;
+      padding-left: 2rem;
+      padding-right: 2rem;
+    }
+
+    .cheatsheet-page-header {
+      margin-top: 2rem;
+      margin-bottom: 2rem;
+      padding-bottom: 1.5rem;
+      border-bottom: 1px solid var(--color-mist);
+    }
+
+    h1 {
+      font-size: 1.5rem;
+      font-weight: 600;
+      margin-bottom: 0.25rem;
+    }
+
+    .subtitle {
+      color: var(--color-ash);
+      font-size: 0.875rem;
+    }
+
+    .category {
+      margin-bottom: 2rem;
+    }
+
+    .category-header {
+      font-size: 0.6875rem;
+      font-weight: 600;
+      text-transform: uppercase;
+      letter-spacing: 0.1em;
+      color: var(--color-ash);
+      margin-bottom: 0.75rem;
+      padding-bottom: 0.5rem;
+      border-bottom: 1px solid var(--color-mist);
+    }
+
+    .commands-grid {
+      display: grid;
+      gap: 0.75rem;
+    }
+
+    .command {
+      display: grid;
+      grid-template-columns: 120px 1fr;
+      gap: 1rem;
+      padding: 0.75rem 0;
+      border-bottom: 1px solid var(--color-mist);
+    }
+
+    .command:last-child {
+      border-bottom: none;
+    }
+
+    .command-name {
+      font-family: var(--font-mono);
+      font-weight: 600;
+      font-size: 0.9375rem;
+      color: var(--color-ink);
+    }
+
+    .command-desc {
+      font-size: 0.875rem;
+      color: var(--color-ink);
+    }
+
+    .command-meta {
+      font-size: 0.75rem;
+      color: var(--color-ash);
+      margin-top: 0.25rem;
+    }
+
+    .command-meta code {
+      font-family: var(--font-mono);
+      font-size: 0.6875rem;
+      background: var(--color-mist);
+      padding: 1px 4px;
+      border-radius: 2px;
+    }
+
+    footer {
+      margin-top: 3rem;
+      padding-top: 1.5rem;
+      border-top: 1px solid var(--color-mist);
+      font-size: 0.75rem;
+      color: var(--color-ash);
+      text-align: center;
+    }
+
+    footer a {
+      color: var(--color-accent);
+      text-decoration: none;
+    }
+
+    @media print {
+      body { padding: 1rem; }
+      .site-header { display: none; }
+      .cheatsheet-page-header,
+      main,
+      footer { max-width: none; padding-left: 0; padding-right: 0; }
+      footer { display: none; }
+      .command { page-break-inside: avoid; }
+    }
+
+    @media (max-width: 600px) {
+      .command {
+        grid-template-columns: 1fr;
+        gap: 0.25rem;
+      }
+    }
+  </style>
+</head>
+<body class="sub-page">
+  <!-- site-header v1 -->
+  <header class="site-header" data-site-header>
+    <a href="/" class="site-header-brand" aria-label="Impeccable home">
+      <svg class="site-header-brand-logo" viewBox="0 0 32 32" aria-hidden="true"><rect width="32" height="32" rx="6" fill="#1a1a1a"/><text x="16" y="24" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="500" fill="#f5f3ef" text-anchor="middle">/</text></svg>
+      <span class="site-header-brand-name">Impeccable</span>
+    </a>
+    <div class="site-header-right">
+      <nav class="site-header-nav" aria-label="Primary">
+        <a href="/" data-nav="home">Home</a>
+        <a href="/skills" data-nav="docs">Docs</a>
+        <a href="/anti-patterns" data-nav="anti-patterns">Anti-Patterns</a>
+        <a href="/visual-mode" data-nav="visual-mode">Visual Mode</a>
+      </nav>
+      <a href="https://github.com/pbakaus/impeccable" class="site-header-github" target="_blank" rel="noopener" aria-label="Impeccable on GitHub, 18k stars">
+        <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
+        <span class="site-header-github-label">18k</span>
+        <svg class="site-header-github-star" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2l2.76 6.36L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.24-.91L12 2z"/></svg>
+      </a>
+    </div>
+  </header>
+
+  <div class="cheatsheet-page-header">
+    <h1>Impeccable Commands</h1>
+    <p class="subtitle">Quick reference for all 18 design commands</p>
+  </div>
+
+  <main id="commands-container">
+    <p style="color: var(--color-ash);">Loading commands...</p>
+  </main>
+
+  <footer>
+    <p><a href="https://impeccable.style">impeccable.style</a> &middot; Design fluency for AI harnesses</p>
+  </footer>
+
+  <script type="module">
+    const categoryLabels = {
+      'diagnostic': 'Diagnostic',
+      'quality': 'Quality',
+      'intensity': 'Intensity',
+      'adaptation': 'Adaptation',
+      'enhancement': 'Enhancement',
+      'system': 'System'
+    };
+
+    const categoryOrder = ['diagnostic', 'quality', 'intensity', 'adaptation', 'enhancement', 'system'];
+
+    const commandCategories = {
+      'impeccable': 'system',
+      'audit': 'diagnostic',
+      'critique': 'diagnostic',
+      'polish': 'quality',
+      'optimize': 'quality',
+      'harden': 'quality',
+      'clarify': 'adaptation',
+      'quieter': 'intensity',
+      'bolder': 'intensity',
+      'distill': 'adaptation',
+      'animate': 'enhancement',
+      'colorize': 'enhancement',
+      'delight': 'enhancement',
+      'adapt': 'adaptation',
+      'typeset': 'enhancement',
+      'layout': 'enhancement',
+      'overdrive': 'enhancement'
+    };
+
+    const commandRelationships = {
+      'impeccable': { flow: 'One-time project context gathering' },
+      'audit': { leadsTo: ['harden', 'optimize', 'adapt', 'clarify'], flow: 'Technical quality audit' },
+      'critique': { leadsTo: ['polish', 'distill', 'bolder', 'quieter', 'typeset', 'layout'], flow: 'UX and design review' },
+      'polish': { flow: 'Final pass and design system alignment' },
+      'optimize': { flow: 'Performance improvements' },
+      'harden': { combinesWith: ['optimize'], flow: 'Edge cases, onboarding, and error handling' },
+      'clarify': { combinesWith: ['polish', 'adapt'], flow: 'Improve UX copy' },
+      'quieter': { pairs: 'bolder', flow: 'Tone down bold designs' },
+      'bolder': { pairs: 'quieter', flow: 'Amplify timid designs' },
+      'distill': { combinesWith: ['quieter', 'polish'], flow: 'Strip to essence' },
+      'animate': { combinesWith: ['delight'], flow: 'Add motion' },
+      'colorize': { combinesWith: ['bolder', 'delight'], flow: 'Add strategic color' },
+      'delight': { combinesWith: ['bolder', 'animate'], flow: 'Add personality' },
+      'adapt': { combinesWith: ['polish', 'clarify'], flow: 'Different devices/contexts' },
+      'typeset': { combinesWith: ['bolder', 'polish'], flow: 'Fix typography' },
+      'layout': { combinesWith: ['distill', 'adapt'], flow: 'Fix layout & spacing' },
+      'overdrive': { combinesWith: ['animate', 'delight'], flow: 'Technically extraordinary effects' }
+    };
+
+    async function loadCommands() {
+      try {
+        const response = await fetch('/api/commands');
+        const commands = await response.json();
+
+        // Group by category
+        const grouped = {};
+        commands.forEach(cmd => {
+          const cat = commandCategories[cmd.id] || 'other';
+          if (!grouped[cat]) grouped[cat] = [];
+          grouped[cat].push(cmd);
+        });
+
+        // Render
+        const container = document.getElementById('commands-container');
+        let html = '';
+
+        categoryOrder.forEach(cat => {
+          if (grouped[cat] && grouped[cat].length > 0) {
+            html += `<section class="category">
+              <h2 class="category-header">${categoryLabels[cat]}</h2>
+              <div class="commands-grid">`;
+
+            grouped[cat].forEach(cmd => {
+              const rel = commandRelationships[cmd.id];
+              let metaHtml = '';
+
+              if (rel) {
+                if (rel.pairs) {
+                  metaHtml = `Pairs with <code>/${rel.pairs}</code>`;
+                } else if (rel.leadsTo) {
+                  metaHtml = `Leads to ${rel.leadsTo.map(c => `<code>/${c}</code>`).join(', ')}`;
+                } else if (rel.combinesWith) {
+                  metaHtml = `Combines with ${rel.combinesWith.map(c => `<code>/${c}</code>`).join(', ')}`;
+                }
+              }
+
+              const isBeta = cmd.id === 'overdrive';
+              html += `<div class="command">
+                <div class="command-name">/${cmd.id}${isBeta ? ' <span style="font-size:0.55rem;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:var(--color-accent,#c8956c);border:1px solid;border-radius:3px;padding:1px 5px;vertical-align:middle;margin-left:4px;">BETA</span>' : ''}</div>
+                <div class="command-info">
+                  <div class="command-desc">${cmd.description}</div>
+                  ${metaHtml ? `<div class="command-meta">${metaHtml}</div>` : ''}
+                </div>
+              </div>`;
+            });
+
+            html += `</div></section>`;
+          }
+        });
+
+        container.innerHTML = html;
+      } catch (error) {
+        console.error('Error loading commands:', error);
+        document.getElementById('commands-container').innerHTML =
+          '<p style="color: red;">Error loading commands. Please try refreshing.</p>';
+      }
+    }
+
+    loadCommands();
+  </script>
+</body>
+</html>

public/css/gallery.css 🔗

@@ -0,0 +1,184 @@
+/* Gallery - Renaissance Edition */
+
+.skills-section {
+  position: relative;
+  padding: var(--spacing-xl) 0;
+  overflow: hidden;
+  background: var(--color-bg);
+}
+
+.skills-gallery {
+  display: block; /* Override grid from previous version */
+  position: relative;
+}
+
+/* The horizontal track */
+.gallery-track {
+  display: flex;
+  gap: var(--spacing-lg);
+  overflow-x: auto;
+  scroll-snap-type: x mandatory;
+  padding: var(--spacing-md) var(--spacing-lg) var(--spacing-xl);
+  -webkit-overflow-scrolling: touch;
+  scrollbar-width: none; /* Firefox */
+  cursor: grab;
+}
+
+.gallery-track:active {
+  cursor: grabbing;
+}
+
+.gallery-track::-webkit-scrollbar {
+  display: none; /* Chrome */
+}
+
+/* The Art Frame */
+.gallery-frame {
+  flex: 0 0 80vw;
+  max-width: 1100px;
+  scroll-snap-align: center;
+  position: relative;
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  box-shadow: 
+    0 4px 6px -1px rgba(0, 0, 0, 0.05),
+    0 20px 50px -10px rgba(0, 0, 0, 0.1);
+  border-radius: 2px; /* Sharp, like a frame */
+  overflow: hidden;
+  opacity: 0.4;
+  transform: scale(0.95);
+  transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out), box-shadow 0.6s var(--ease-out);
+}
+
+/* Active State (Center) */
+.gallery-frame.active {
+  opacity: 1;
+  transform: scale(1);
+  box-shadow: 
+    0 20px 25px -5px rgba(0, 0, 0, 0.1),
+    0 40px 100px -20px rgba(0, 0, 0, 0.2);
+  border-color: var(--color-charcoal);
+  border-width: 1px;
+}
+
+/* Frame Content Layout */
+.gallery-content {
+  display: grid;
+  grid-template-columns: 1.2fr 1fr;
+  height: 600px; /* Fixed height for consistency */
+}
+
+@media (max-width: 900px) {
+  .gallery-frame {
+    flex: 0 0 90vw;
+  }
+  .gallery-content {
+    grid-template-columns: 1fr;
+    height: auto;
+    min-height: 600px;
+  }
+}
+
+/* Visual Side (Demo) */
+.gallery-visual {
+  background: var(--color-cream);
+  border-right: 1px solid var(--color-mist);
+  position: relative;
+  overflow: hidden;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: var(--spacing-lg);
+}
+
+/* Info Side */
+.gallery-info {
+  padding: var(--spacing-xl);
+  display: flex;
+  flex-direction: column;
+  overflow-y: auto;
+}
+
+.gallery-header {
+  margin-bottom: var(--spacing-lg);
+}
+
+.gallery-title {
+  font-family: var(--font-display);
+  font-size: 2.5rem;
+  font-style: italic;
+  margin: 0 0 var(--spacing-xs);
+  color: var(--color-ink);
+}
+
+.gallery-meta {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  color: var(--color-ash);
+}
+
+.gallery-desc {
+  font-size: 1.125rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  margin-bottom: var(--spacing-xl);
+  max-width: 45ch;
+}
+
+/* Tags within frame */
+.gallery-tags {
+  display: flex;
+  flex-wrap: wrap;
+  gap: var(--spacing-xs);
+  margin-top: auto;
+}
+
+.gallery-tag {
+  padding: 6px 12px;
+  border: 1px solid var(--color-mist);
+  border-radius: 4px;
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+}
+
+/* Map Navigation */
+.gallery-map {
+  display: flex;
+  justify-content: center;
+  gap: 8px;
+  margin-top: var(--spacing-lg);
+}
+
+.gallery-dot {
+  width: 40px;
+  height: 2px;
+  background: var(--color-mist);
+  cursor: pointer;
+  transition: all 0.3s ease;
+  position: relative;
+  border: none;
+  padding: 0;
+  font: inherit;
+}
+
+.gallery-dot::after {
+  content: '';
+  position: absolute;
+  top: -10px;
+  bottom: -10px;
+  left: 0;
+  right: 0;
+}
+
+.gallery-dot:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 4px;
+  border-radius: 1px;
+}
+
+.gallery-dot.active {
+  background: var(--color-accent);
+  height: 4px;
+}

public/css/main.css 🔗

@@ -0,0 +1,3758 @@
+/*
+ * impeccable.style — Renaissance Redesign
+ *
+ * Editorial precision meets fluid motion.
+ * Light, high-contrast, typographically bold.
+ */
+
+/* Shared design tokens + minimal reset */
+@import "./tokens.css";
+
+/* Section partials (landing page only) */
+@import "./problem-section.css";
+@import "./workflow.css";
+@import "./gallery.css";
+@import "./skill-demos.css";
+
+
+/* ============================================
+   SKIP LINK (Accessibility)
+   ============================================ */
+
+.skip-link {
+  position: absolute;
+  top: -100%;
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 10000;
+  padding: var(--spacing-sm) var(--spacing-lg);
+  background: var(--color-ink);
+  color: var(--color-paper);
+  font-weight: 600;
+  text-decoration: none;
+  border-radius: 0 0 8px 8px;
+  transition: top 0.2s ease;
+}
+
+.skip-link:focus {
+  top: 0;
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+
+/* ============================================
+   FLOATING BOTTOM NAV
+   ============================================ */
+
+.section-nav {
+  position: fixed;
+  bottom: var(--spacing-md);
+  left: 50%;
+  transform: translateX(-50%) translateY(20px);
+  z-index: 100;
+  display: flex;
+  align-items: center;
+  gap: 2px;
+  padding: 4px;
+  background: oklch(98% 0 0 / 0.85);
+  backdrop-filter: blur(12px);
+  -webkit-backdrop-filter: blur(12px);
+  border: 1px solid var(--color-mist);
+  border-radius: 100px;
+  box-shadow: 0 4px 24px -4px rgba(0,0,0,0.12), 0 1px 3px rgba(0,0,0,0.06);
+  opacity: 0;
+  pointer-events: none;
+  transition: opacity 0.4s var(--ease-out), transform 0.4s var(--ease-out);
+}
+
+.section-nav.is-visible {
+  opacity: 1;
+  pointer-events: auto;
+  transform: translateX(-50%) translateY(0);
+}
+
+.section-nav-item {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  padding: 8px 12px;
+  text-decoration: none;
+  border-radius: 100px;
+  transition: background 0.2s ease, color 0.2s ease;
+}
+
+.section-nav-item:hover {
+  background: var(--color-mist);
+  text-decoration: none;
+}
+
+.section-nav-item.is-active {
+  background: var(--color-ink);
+}
+
+.section-nav-item.is-active .section-nav-num {
+  color: var(--color-paper);
+}
+
+.section-nav-item.is-active .section-nav-label {
+  color: var(--color-paper);
+}
+
+.section-nav-num {
+  font-family: var(--font-mono);
+  font-size: 0.5625rem;
+  font-weight: 500;
+  color: var(--color-ash);
+  letter-spacing: 0.02em;
+  transition: color 0.2s ease;
+}
+
+.section-nav-label {
+  font-family: var(--font-body);
+  font-size: 0.6875rem;
+  font-weight: 500;
+  color: var(--color-charcoal);
+  transition: color 0.2s ease;
+  white-space: nowrap;
+}
+
+@media (max-width: 700px) {
+  .section-nav-label {
+    display: none;
+  }
+  .section-nav-item {
+    padding: 8px 10px;
+  }
+}
+
+@media (max-width: 400px) {
+  .section-nav-item {
+    padding: 8px;
+  }
+
+  .section-nav-num {
+    font-size: 0.625rem;
+  }
+}
+
+
+/* ============================================
+   BASE STYLES
+   ============================================ */
+
+html {
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-rendering: optimizeLegibility;
+  overflow-x: clip; /* Prevent horizontal scroll without breaking position:sticky */
+}
+
+body {
+  font-family: var(--font-body);
+  font-size: 16px;
+  line-height: 1.625;
+  color: var(--color-text);
+  background: var(--color-paper);
+  overflow-x: clip;
+  min-height: 100vh;
+  min-height: 100dvh;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  font-family: var(--font-display);
+  font-weight: 400;
+  line-height: 1.1;
+  letter-spacing: -0.02em;
+  color: var(--color-ink);
+}
+
+a {
+  color: var(--color-accent);
+  text-decoration: underline;
+  text-decoration-thickness: 1px;
+  text-underline-offset: 2px;
+  transition: color var(--duration-fast) var(--ease-out), text-decoration-color var(--duration-fast) var(--ease-out);
+}
+
+a:hover {
+  color: var(--color-accent-hover);
+  text-decoration-thickness: 2px;
+}
+
+/* Remove underline for buttons styled as links */
+.btn,
+.footer-logo,
+[class*="nav-item"] {
+  text-decoration: none;
+}
+
+strong {
+  font-weight: 600;
+  color: var(--color-ink);
+}
+
+code {
+  font-family: var(--font-mono);
+  font-size: 0.9em;
+  padding: 0.15em 0.4em;
+  background: var(--color-accent-dim);
+  color: var(--color-accent);
+  border-radius: 4px;
+}
+
+::selection {
+  background: var(--color-accent-soft);
+  color: var(--color-ink);
+}
+
+/* ============================================
+   GRAIN OVERLAY
+   ============================================ */
+
+.grain-overlay {
+  position: fixed;
+  inset: 0;
+  pointer-events: none;
+  z-index: 9999;
+  opacity: 0.03;
+  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
+  background-repeat: repeat;
+}
+
+/* ============================================
+   LAYOUT
+   ============================================ */
+
+.site-content {
+  max-width: var(--width-max);
+  margin: 0 auto;
+  padding: 0 var(--spacing-lg);
+}
+
+@media (max-width: 768px) {
+  .site-content {
+    padding: 0 var(--spacing-md);
+  }
+}
+
+/* ============================================
+   SECTION HEADERS
+   ============================================ */
+
+.section-header {
+  margin-bottom: var(--spacing-lg);
+  position: relative;
+}
+
+.section-number {
+  display: block;
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 500;
+  letter-spacing: 0.05em;
+  color: var(--color-ash);
+  margin-bottom: var(--spacing-xs);
+  text-transform: uppercase;
+}
+
+.section-title {
+  font-size: clamp(1.75rem, 4vw, 2.5rem);
+  font-weight: 400;
+  line-height: 1.2;
+  margin: 0;
+}
+
+.section-subtitle {
+  font-size: 1rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  margin-top: var(--spacing-sm);
+  max-width: 55ch;
+}
+
+.cheatsheet-link {
+  color: var(--color-accent);
+  text-decoration: none;
+  font-size: 0.875rem;
+  margin-left: 0.5em;
+}
+
+.cheatsheet-link:hover {
+  text-decoration: underline;
+}
+
+.section-lead {
+  font-size: 1rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  max-width: 55ch;
+  margin-bottom: var(--spacing-lg);
+}
+
+/* ============================================
+   HERO SECTION - Combined Layout
+   ============================================ */
+
+.hero-combined {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  min-height: 100vh;
+  min-height: 100dvh;
+  padding: var(--spacing-lg) 0 var(--spacing-2xl);
+  background: var(--color-paper);
+}
+
+.github-link {
+  position: absolute;
+  top: var(--spacing-md);
+  right: var(--spacing-lg);
+  z-index: 10;
+  color: var(--color-ash);
+  transition: color 0.2s ease;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  text-decoration: none;
+}
+
+.github-link:hover {
+  color: var(--color-ink);
+  text-decoration: none;
+}
+
+.github-stars {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  font-weight: 500;
+  color: var(--color-ash);
+  transition: color 0.2s ease;
+}
+
+.github-link:hover .github-stars {
+  color: var(--color-ink);
+}
+
+.hero-combined-container {
+  max-width: var(--width-max);
+  margin: 0 auto;
+  padding: 0 var(--spacing-lg);
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: var(--spacing-xl);
+  align-items: center;
+  width: 100%;
+}
+
+@media (max-width: 1024px) {
+  .hero-combined-container {
+    grid-template-columns: 1fr;
+    gap: var(--spacing-lg);
+    text-align: center;
+  }
+}
+
+@media (max-width: 768px) {
+  .hero-combined-right {
+    order: -1;
+    padding-top: var(--spacing-xl);
+  }
+
+  .hero-combined-container {
+    gap: var(--spacing-md);
+  }
+
+  .hero-combined-left {
+    gap: var(--spacing-sm);
+    padding-top: var(--spacing-md);
+  }
+
+  .hero-hook-text,
+  .hero-included-box,
+  .hero-cta-group {
+    margin-top: var(--spacing-xs);
+  }
+}
+
+.hero-combined-left {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-md);
+}
+
+@media (max-width: 1024px) {
+  .hero-combined-left {
+    align-items: center;
+  }
+}
+
+.hero-title-combined {
+  font-family: var(--font-display);
+  font-size: clamp(2.5rem, 7vw, 4.5rem);
+  font-weight: 300;
+  font-style: italic;
+  line-height: 1;
+  letter-spacing: -0.02em;
+  margin: 0;
+  color: var(--color-ink);
+}
+
+.hero-tagline-combined {
+  font-family: var(--font-display);
+  font-size: clamp(1.125rem, 2.5vw, 1.75rem);
+  font-weight: 400;
+  font-style: italic;
+  line-height: 1.3;
+  margin: 0;
+  color: var(--color-charcoal);
+}
+
+.hero-hook-text {
+  font-size: 1rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  max-width: 45ch;
+  margin: 0;
+}
+
+.hero-hook-text--short {
+  display: none;
+}
+
+@media (max-width: 768px) {
+  .hero-hook-text--full {
+    display: none;
+  }
+
+  .hero-hook-text--short {
+    display: block;
+  }
+}
+
+/* Hero Included Box - Subtle "What's included" */
+.hero-included-box {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  padding: 10px 14px;
+  border: 1px solid var(--color-mist);
+  background: transparent;
+  max-width: 45ch;
+}
+
+.hero-included-title {
+  font-family: var(--font-body);
+  font-size: 0.5625rem;
+  font-weight: 500;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  color: var(--color-ash);
+}
+
+.hero-included-items {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 6px;
+  font-size: 0.8125rem;
+  color: var(--color-charcoal);
+  line-height: 1.5;
+}
+
+.hero-included-items em {
+  font-style: normal;
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+}
+
+.hero-included-sep {
+  color: var(--color-mist);
+}
+
+@media (max-width: 500px) {
+  .hero-included-items {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 4px;
+  }
+
+  .hero-included-sep {
+    display: none;
+  }
+}
+
+.hero-cta-group {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-lg);
+  margin-top: var(--spacing-sm);
+}
+
+@media (max-width: 600px) {
+  .hero-cta-group {
+    flex-direction: column;
+    gap: var(--spacing-md);
+  }
+}
+
+.hero-cta-combined {
+  display: inline-block;
+  padding: var(--spacing-sm) var(--spacing-xl);
+  font-family: var(--font-body);
+  font-size: 0.9rem;
+  font-weight: 500;
+  letter-spacing: 0.05em;
+  text-transform: uppercase;
+  text-decoration: none;
+  color: var(--color-paper);
+  background: var(--color-ink);
+  border: none;
+  transition: transform 0.2s ease, background 0.2s ease;
+}
+
+.hero-cta-combined:hover {
+  transform: translateY(-2px);
+  background: var(--color-accent);
+  color: var(--color-paper);
+}
+
+.hero-logos-inline {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  gap: 6px;
+}
+
+.hero-logos-inline .hero-logos-label {
+  font-size: 0.6875rem;
+  color: var(--color-ash);
+  letter-spacing: 0.03em;
+}
+
+.hero-logos-inline .hero-logos-row {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex-wrap: wrap;
+}
+
+.hero-logos-inline .hero-logos-row img {
+  border-radius: 4px;
+  opacity: 0.7;
+  transition: opacity 0.2s ease;
+}
+
+.hero-logos-inline .hero-logos-row img:hover {
+  opacity: 1;
+}
+
+.hero-combined-right {
+  display: flex;
+  justify-content: center;
+}
+
+.hero-combined-right .split-comparison {
+  max-width: 520px;
+  width: 100%;
+}
+
+.hero-combined-right .split-container {
+  max-width: 100%;
+}
+
+/* Bias tags at bottom of hero */
+.hero-bias-tags {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: var(--spacing-xs);
+  margin-top: var(--spacing-lg);
+  padding-top: var(--spacing-md);
+  border-top: 1px solid var(--color-mist);
+  max-width: var(--width-max);
+  margin-left: auto;
+  margin-right: auto;
+  width: 100%;
+  padding-bottom: var(--spacing-md);
+}
+
+/* ============================================
+   PROBLEM SECTION - Clarity Lens
+   ============================================ */
+
+.problem-section {
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.problem-content {
+  display: grid;
+  gap: var(--spacing-xl);
+}
+
+/* Split Screen Comparison */
+.split-comparison {
+  position: relative;
+  width: 100%;
+  max-width: 600px;
+  margin: 0 auto;
+  padding: 20px;
+  margin-top: -20px;
+  margin-bottom: -20px;
+}
+
+.split-container {
+  position: relative;
+  width: 100%;
+  max-width: 500px;
+  height: 380px;
+  margin: 0 auto;
+  border-radius: 12px;
+  overflow: hidden;
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  cursor: ew-resize;
+  user-select: none;
+}
+
+.split-before,
+.split-after {
+  position: absolute;
+  inset: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.split-before {
+  z-index: 1;
+}
+
+.split-content {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.split-after {
+  /* Angled clip-path matching divider's skewX(-10deg): top-right, bottom-left */
+  clip-path: polygon(58% 0%, 100% 0%, 100% 100%, 42% 100%);
+  z-index: 2;
+  background: var(--color-paper);
+}
+
+.split-divider {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 50%;
+  width: 3px;
+  background: var(--color-accent);
+  transform: translateX(-50%) skewX(-10deg);
+  pointer-events: none;
+  z-index: 3;
+  box-shadow: 0 0 20px rgba(0,0,0,0.15);
+}
+
+.split-label {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%) skewX(10deg);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: var(--color-paper);
+  background: var(--color-accent);
+  padding: 6px 14px;
+  border-radius: 4px;
+  white-space: nowrap;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
+}
+
+/* Slop Card - Generic AI output */
+.slop-card {
+  width: 280px;
+  height: 280px;
+  background: linear-gradient(135deg, #f5f3ff 0%, #ede9fe 50%, #ddd6fe 100%);
+  border-radius: 16px;
+  padding: 24px;
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
+  font-family: 'Inter', system-ui, sans-serif;
+  display: flex;
+  flex-direction: column;
+}
+
+.slop-header {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin-bottom: 16px;
+}
+
+.slop-avatar {
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
+  flex-shrink: 0;
+}
+
+.slop-text { flex: 1; }
+.slop-title { font-size: 14px; font-weight: 600; color: #1f2937; margin-bottom: 2px; }
+.slop-subtitle { font-size: 12px; color: #6b7280; }
+.slop-body { font-size: 13px; line-height: 1.5; color: #4b5563; margin-bottom: auto; flex: 1; }
+
+.slop-button {
+  width: 100%;
+  padding: 10px 20px;
+  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
+  color: white;
+  border: none;
+  border-radius: 8px;
+  font-family: 'Inter', system-ui, sans-serif;
+  font-size: 13px;
+  font-weight: 500;
+  cursor: pointer;
+  margin-top: auto;
+}
+
+/* Callout labels pointing to issues */
+.slop-callouts {
+  position: absolute;
+  inset: 0;
+  pointer-events: none;
+}
+
+.slop-callout {
+  position: absolute;
+  font-size: 0.625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.06em;
+  color: var(--color-accent);
+  background: var(--color-paper);
+  padding: 4px 8px;
+  border: 1px solid var(--color-accent);
+  border-radius: 3px;
+  white-space: nowrap;
+  opacity: 0;
+  animation: calloutFadeIn 0.4s var(--ease-out) forwards;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+}
+
+.slop-callout[data-point="font"] {
+  top: 15%;
+  right: 5%;
+  animation-delay: 0.1s;
+}
+
+.slop-callout[data-point="gradient"] {
+  top: 40%;
+  left: 5%;
+  animation-delay: 0.25s;
+}
+
+.slop-callout[data-point="copy"] {
+  bottom: 35%;
+  right: 8%;
+  animation-delay: 0.4s;
+}
+
+.slop-callout[data-point="rounded"] {
+  bottom: 12%;
+  left: 10%;
+  animation-delay: 0.55s;
+}
+
+@keyframes calloutFadeIn {
+  from {
+    opacity: 0;
+    transform: scale(0.9);
+  }
+  to {
+    opacity: 1;
+    transform: scale(1);
+  }
+}
+
+/* Impeccable Card - With Design Skills */
+.impeccable-card {
+  width: 280px;
+  height: 300px;
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  padding: var(--spacing-lg);
+  text-align: left;
+  display: flex;
+  flex-direction: column;
+}
+
+.impeccable-eyebrow {
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 500;
+  letter-spacing: 0.15em;
+  text-transform: uppercase;
+  color: var(--color-accent);
+  margin-bottom: var(--spacing-xs);
+}
+
+.impeccable-title {
+  font-family: var(--font-display);
+  font-size: 1.75rem;
+  font-weight: 300;
+  font-style: italic;
+  color: var(--color-ink);
+  margin-bottom: var(--spacing-sm);
+  line-height: 1.1;
+}
+
+.impeccable-body {
+  font-size: 0.875rem;
+  line-height: 1.6;
+  color: var(--color-ash);
+  margin-bottom: auto;
+  flex: 1;
+}
+
+.impeccable-button {
+  display: inline-flex;
+  margin-top: var(--spacing-sm);
+  padding: 0.625rem 1.5rem;
+  background: var(--color-ink);
+  color: var(--color-paper);
+  border: none;
+  font-family: var(--font-body);
+  font-size: 0.8125rem;
+  font-weight: 500;
+  letter-spacing: 0.03em;
+  cursor: pointer;
+  transition: all var(--duration-base) var(--ease-out);
+  align-self: flex-start;
+}
+
+.impeccable-button:hover {
+  background: var(--color-accent);
+}
+
+/* Split Labels */
+.split-labels {
+  display: flex;
+  justify-content: center;
+  gap: var(--spacing-xl);
+  margin-top: var(--spacing-md);
+}
+
+.split-label-item {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-xs);
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+}
+
+@media (max-width: 768px) {
+  .split-comparison {
+    padding: 8px;
+    margin-top: -8px;
+    margin-bottom: -8px;
+  }
+
+  .split-labels {
+    display: none;
+  }
+}
+
+.split-label-dot {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  background: var(--color-mist);
+}
+
+.split-label-dot--accent {
+  background: var(--color-accent);
+}
+
+/* ============================================
+   FOUNDATION SECTION
+   ============================================ */
+
+.foundation-section {
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.foundation-content {
+  display: grid;
+  gap: var(--spacing-lg);
+}
+
+.foundation-grid {
+  display: flex;
+  align-items: flex-end;
+  height: 600px;
+  gap: var(--spacing-sm);
+  margin-top: -80px; /* Pull up into the header whitespace */
+  position: relative;
+}
+
+.foundation-column {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  justify-content: flex-end;
+  cursor: pointer;
+  min-width: 0;
+}
+
+.foundation-card {
+  padding: var(--spacing-md);
+  background: white;
+  border: 1px solid var(--color-bg);
+  border-radius: 12px;
+  display: flex;
+  flex-direction: column;
+  height: 280px;
+  margin-bottom: var(--spacing-xs);
+  transition: transform var(--duration-slow) var(--ease-out-quint), border-color var(--duration-base) var(--ease-out), box-shadow var(--duration-base) var(--ease-out);
+  position: relative;
+  z-index: 2;
+  box-sizing: border-box;
+}
+
+.foundation-column:hover .foundation-card {
+  transform: translateY(-12px);
+  border-color: var(--color-accent);
+  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
+}
+
+.foundation-plinth {
+  width: 100%;
+  background: repeating-linear-gradient(45deg, var(--color-bg), var(--color-bg) 2px, transparent 2px, transparent 10px);
+  border: 1px solid var(--color-mist);
+  border-bottom: none;
+  border-radius: 8px 8px 0 0;
+  transition: all var(--duration-base) var(--ease-out);
+}
+
+.foundation-column:hover .foundation-plinth {
+  background: var(--color-mist);
+  opacity: 0.7;
+}
+
+/* Plinth Heights - More dramatic steps */
+.plinth-1 { height: 4%; }
+.plinth-2 { height: 12%; }
+.plinth-3 { height: 20%; }
+.plinth-4 { height: 28%; }
+.plinth-5 { height: 36%; }
+.plinth-6 { height: 44%; }
+.plinth-7 { height: 52%; }
+
+.foundation-card-viz {
+  height: 52px;
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  color: var(--color-ink);
+  margin-bottom: var(--spacing-md);
+}
+
+.foundation-card-header {
+  display: flex;
+  align-items: baseline;
+  gap: var(--spacing-xs);
+  margin-bottom: 8px;
+  overflow: hidden;
+}
+
+.foundation-card-label {
+  font-family: var(--font-display);
+  font-size: 1.35rem;
+  color: var(--color-ink);
+  line-height: 1.1;
+}
+
+.foundation-card-count {
+  font-family: var(--font-mono);
+  font-size: 0.7rem;
+  color: var(--color-accent);
+  flex-shrink: 0;
+}
+
+.foundation-card-detail {
+  font-size: 0.875rem;
+  color: var(--color-ash);
+  line-height: 1.4;
+  margin: auto 0 0 0;
+}
+
+.foundation-svg {
+  width: 44px;
+  height: 44px;
+}
+
+/* Typography & Spatial (Breathing draw + full draw on hover) */
+.anim-draw { stroke-dasharray: 100; stroke-dashoffset: 100; animation: draw-breathe 4s ease-in-out infinite; }
+.foundation-column:hover .anim-draw { animation: draw-in 0.8s var(--ease-out) forwards; }
+.anim-draw-delay { stroke-dasharray: 100; stroke-dashoffset: 100; }
+.foundation-column:hover .anim-draw-delay { animation: draw-in 1s var(--ease-out) 0.2s forwards; }
+@keyframes draw-breathe { 0%, 100% { stroke-dashoffset: 100; } 50% { stroke-dashoffset: 40; } }
+@keyframes draw-in { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
+
+/* Color & Contrast (Breathing pulse + spread on hover) */
+.anim-move-x { animation: pulse-x 3s ease-in-out infinite; }
+.foundation-column:hover .anim-move-x { animation: spread-x 0.6s var(--ease-in-out) forwards; }
+.anim-move-x-opp { animation: pulse-x-opp 3s ease-in-out infinite; }
+.foundation-column:hover .anim-move-x-opp { animation: spread-x-opp 0.6s var(--ease-in-out) forwards; }
+.anim-fade-in { opacity: 0; transition: opacity 0.6s var(--ease-in-out); }
+.foundation-column:hover .anim-fade-in { opacity: 1; }
+@keyframes pulse-x { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(1.5px); } }
+@keyframes pulse-x-opp { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(-1.5px); } }
+@keyframes spread-x { from { transform: translateX(0); } to { transform: translateX(4px); } }
+@keyframes spread-x-opp { from { transform: translateX(0); } to { transform: translateX(-4px); } }
+
+/* Responsive (Staggered reflow from desktop 2-col to mobile stack) */
+.anim-res-frame, .anim-res-img, .anim-res-title, .anim-res-line-1, .anim-res-line-2 {
+  transform-box: fill-box;
+  transform-origin: 50% 50%;
+  transition: transform 0.4s var(--ease-in-out);
+}
+.anim-res-frame { transition-delay: 0s; }
+.anim-res-img { transition-delay: 0s; }
+.anim-res-title { transition-delay: 0s; }
+.anim-res-line-1 { transition-delay: 0s; }
+.anim-res-line-2 { transition-delay: 0s; }
+.foundation-column:hover .anim-res-frame { transform: scaleX(0.57) scaleY(1.17); transition-delay: 0s; }
+.foundation-column:hover .anim-res-img { transform: translate(6px, -4px) scale(0.65); transition-delay: 0.05s; }
+.foundation-column:hover .anim-res-title { transform: translate(-7px, 6px) scaleX(0.65); transition-delay: 0.1s; }
+.foundation-column:hover .anim-res-line-1 { transform: translate(-7px, 4.75px) scaleX(0.65); transition-delay: 0.15s; }
+.foundation-column:hover .anim-res-line-2 { transform: translate(-6px, 4.25px) scaleX(0.6); transition-delay: 0.2s; }
+
+/* Interaction (Gentle wobble + full toggle on hover) */
+.anim-toggle-move { animation: toggle-wobble 3s ease-in-out infinite; }
+.foundation-column:hover .anim-toggle-move { animation: toggle-snap 0.35s var(--ease-in-out) forwards; }
+@keyframes toggle-wobble { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(2px); } }
+@keyframes toggle-snap { from { transform: translateX(0); fill: var(--color-mist); } to { transform: translateX(8px); fill: var(--color-accent); } }
+
+/* Motion (Gentle bob + full bounce on hover) */
+.anim-squash-ball { transform-origin: 20px 20px; animation: ball-bob 2.5s ease-in-out infinite; }
+.foundation-column:hover .anim-squash-ball { animation: bounce-ball 1.5s linear infinite; }
+@keyframes ball-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(3px); } }
+@keyframes bounce-ball {
+  0% { transform: translateY(0); }
+  6% { transform: translateY(0.5px); }
+  18% { transform: translateY(4px); }
+  35% { transform: translateY(12px); }
+  42% { transform: translateY(12px) scaleX(1.3) scaleY(0.6); }
+  48% { transform: translateY(12px); }
+  65% { transform: translateY(4px); }
+  78% { transform: translateY(0.5px); }
+  88%, 100% { transform: translateY(0); }
+}
+
+/* UX Writing (Cursor always blinks) */
+.anim-blink { animation: blink-key 1s step-end infinite; }
+@keyframes blink-key { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
+
+/* Tablet: 3-col grid (switch before stairs clip) */
+@media (max-width: 1200px) {
+  .foundation-grid {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    height: auto;
+    gap: var(--spacing-md);
+    margin-top: var(--spacing-xl);
+  }
+  .foundation-column { height: auto; }
+  .foundation-card { height: auto; min-height: 200px; }
+  .foundation-plinth { display: none; }
+  .foundation-column:hover .foundation-card { transform: translateY(-4px); }
+
+  /* Enable hover animations on card hover for grid */
+  .foundation-card:hover .anim-draw { animation: draw-in 0.8s var(--ease-out) forwards; }
+  .foundation-card:hover .anim-draw-delay { animation: draw-in 1s var(--ease-out) 0.2s forwards; }
+  .foundation-card:hover .anim-move-x { animation: spread-x 0.6s var(--ease-in-out) forwards; }
+  .foundation-card:hover .anim-move-x-opp { animation: spread-x-opp 0.6s var(--ease-in-out) forwards; }
+  .foundation-card:hover .anim-fade-in { opacity: 1; }
+  .foundation-card:hover .anim-res-frame { transform: scaleX(0.57) scaleY(1.17); }
+  .foundation-card:hover .anim-res-img { transform: translate(6px, -4px) scale(0.65); }
+  .foundation-card:hover .anim-res-title { transform: translate(-7px, 6px) scaleX(0.65); }
+  .foundation-card:hover .anim-res-line-1 { transform: translate(-7px, 4.75px) scaleX(0.65); }
+  .foundation-card:hover .anim-res-line-2 { transform: translate(-6px, 4.25px) scaleX(0.6); }
+  .foundation-card:hover .anim-toggle-move { animation: toggle-snap 0.35s var(--ease-in-out) forwards; }
+  .foundation-card:hover .anim-squash-ball { animation: bounce-ball 1.5s linear infinite; }
+}
+
+/* Small tablet: 2-col grid */
+@media (max-width: 768px) {
+  .foundation-grid { grid-template-columns: repeat(2, 1fr); }
+}
+
+/* Small mobile: compact horizontal cards */
+@media (max-width: 520px) {
+  .foundation-grid {
+    grid-template-columns: 1fr;
+    gap: var(--spacing-sm);
+  }
+  .foundation-card {
+    display: grid;
+    grid-template-columns: auto 1fr;
+    grid-template-rows: auto auto;
+    align-items: start;
+    min-height: auto;
+    padding: var(--spacing-sm) var(--spacing-md);
+    gap: 0 var(--spacing-md);
+  }
+  .foundation-card-viz {
+    grid-row: 1 / -1;
+    align-self: center;
+    height: auto;
+    margin-bottom: 0;
+  }
+  .foundation-card-header { margin-bottom: 2px; }
+  .foundation-card-label { font-size: 1.1rem; }
+  .foundation-card-detail { margin: 0; font-size: 0.8125rem; }
+}
+
+.foundation-cta {
+  text-align: center;
+}
+
+.foundation-cta-text {
+  font-size: 0.9375rem;
+  color: var(--color-charcoal);
+  margin: 0;
+}
+
+.foundation-cta-text code {
+  font-family: var(--font-mono);
+  font-size: 0.875rem;
+  color: var(--color-ink);
+}
+
+.foundation-slash {
+  color: var(--color-accent);
+}
+
+/* ============================================
+   LANGUAGE SECTION (wraps periodic table + commands)
+   ============================================ */
+
+.language-section {
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.language-content {
+  display: grid;
+  gap: var(--spacing-lg);
+}
+
+/* Grid and flex items default to min-width: auto, which lets them expand to
+   fit their intrinsic content. The mobile commands layout has a split-compare
+   demo wide enough to blow out the entire section on mobile (~2400px wide).
+   Cascade min-width: 0 through every layer of the language section so the
+   intrinsic width can't propagate up the parent chain. */
+.language-section,
+.language-section * {
+  min-width: 0;
+}
+
+.language-content .section-lead {
+  margin-bottom: 0;
+}
+
+.commands-subsection {
+  display: grid;
+  gap: var(--spacing-md);
+}
+
+.commands-header-subtitle {
+  font-size: 0.9375rem;
+  color: var(--color-warm-gray);
+  margin: 0;
+}
+
+/* ============================================
+   VISUAL MODE SECTION
+   ============================================ */
+
+.visual-mode-section {
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.visual-mode-content {
+  display: grid;
+  gap: var(--spacing-lg);
+}
+
+.visual-mode-demo {
+  display: grid;
+  grid-template-columns: 3fr 2fr;
+  gap: var(--spacing-xl);
+  align-items: start;
+}
+
+@media (max-width: 900px) {
+  .visual-mode-demo {
+    grid-template-columns: 1fr;
+  }
+}
+
+/* Browser chrome frame for the iframe */
+.visual-mode-preview {
+  border-radius: 8px;
+  overflow: hidden;
+  border: 1px solid var(--color-mist);
+  box-shadow: 0 8px 30px -6px rgba(0,0,0,0.12);
+}
+
+.visual-mode-preview-header {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  padding: 10px 14px;
+  background: var(--color-cream);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.visual-mode-preview-dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+}
+
+.visual-mode-preview-dot.red { background: #ff5f56; }
+.visual-mode-preview-dot.yellow { background: #ffbd2e; }
+.visual-mode-preview-dot.green { background: #27c93f; }
+
+.visual-mode-preview-title {
+  margin-left: auto;
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  color: var(--color-ash);
+}
+
+.visual-mode-iframe {
+  display: block;
+  width: 100%;
+  height: 480px;
+  border: none;
+  background: white;
+}
+
+/* Details sidebar — editorial blocks, no cards.
+   The live detection overlay (left iframe) is the visual anchor for the
+   whole section; the right column should be quiet text + a single image
+   for the Chrome extension CTA. Hierarchy via typography + space, not
+   bordered containers. */
+.visual-mode-details {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-xl);
+  padding: var(--spacing-md) 0;
+}
+
+.visual-mode-feature {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-xs);
+}
+
+.visual-mode-feature-label {
+  display: block;
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--color-accent);
+}
+
+.visual-mode-feature p {
+  font-size: 0.9375rem;
+  line-height: 1.55;
+  color: var(--color-charcoal);
+  margin: 0;
+}
+
+.visual-mode-feature code {
+  font-family: var(--font-mono);
+  font-size: 0.875rem;
+  color: var(--color-ink);
+}
+
+/* ============================================
+   SOLUTION SECTION (legacy styles for periodic table)
+   ============================================ */
+
+.solution-section {
+  padding: var(--spacing-2xl) 0;
+}
+
+.solution-content {
+  display: grid;
+  gap: var(--spacing-lg);
+}
+
+.solution-content .section-lead {
+  margin-bottom: 0;
+}
+
+/* Two-pillar visual layout */
+.solution-visual {
+  display: grid;
+  grid-template-columns: 1fr auto 1fr;
+  gap: var(--spacing-lg);
+  align-items: stretch;
+}
+
+@media (max-width: 900px) {
+  .solution-visual {
+    grid-template-columns: 1fr;
+    gap: var(--spacing-md);
+  }
+}
+
+/* Interactive Framework Viz - Periodic Table */
+.solution-visual-interactive {
+  width: 100%;
+  min-height: 380px;
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 8px;
+  position: relative;
+  overflow: visible;
+}
+
+.solution-pillar {
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  padding: var(--spacing-lg);
+  transition: all var(--duration-base) var(--ease-out);
+}
+
+.solution-pillar:hover {
+  border-color: var(--color-accent);
+  transform: translateY(-4px);
+  box-shadow: 0 20px 60px var(--color-accent-dim);
+}
+
+.pillar-header {
+  text-align: center;
+  margin-bottom: var(--spacing-lg);
+  padding-bottom: var(--spacing-md);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.pillar-icon {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 56px;
+  height: 56px;
+  border-radius: 50%;
+  background: var(--color-accent-dim);
+  color: var(--color-accent);
+  margin-bottom: var(--spacing-sm);
+}
+
+.pillar-title {
+  font-family: var(--font-display);
+  font-size: 1.75rem;
+  font-weight: 400;
+  margin: 0 0 var(--spacing-xs);
+}
+
+.pillar-subtitle {
+  font-size: 0.875rem;
+  color: var(--color-ash);
+  margin: 0;
+}
+
+.pillar-content {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-sm);
+}
+
+.pillar-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: var(--spacing-sm);
+  background: var(--color-paper);
+  border-radius: 4px;
+  transition: all var(--duration-fast) var(--ease-out);
+}
+
+.pillar-item:hover {
+  background: var(--color-accent-dim);
+}
+
+.pillar-item-name {
+  font-weight: 500;
+  color: var(--color-ink);
+  font-size: 0.9375rem;
+}
+
+.pillar-item-code {
+  font-family: var(--font-mono);
+  font-size: 0.875rem;
+  font-weight: 500;
+  color: var(--color-accent);
+  background: transparent;
+  padding: 0;
+}
+
+.pillar-item-desc {
+  font-size: 0.75rem;
+  color: var(--color-ash);
+}
+
+.pillar-item--more {
+  justify-content: center;
+  font-size: 0.8125rem;
+  font-weight: 500;
+  color: var(--color-accent);
+  background: transparent;
+  border: 1px dashed var(--color-mist);
+}
+
+/* Connector between pillars */
+.solution-connector {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.connector-plus {
+  font-family: var(--font-display);
+  font-size: 3rem;
+  font-weight: 300;
+  color: var(--color-accent);
+  opacity: 0.5;
+}
+
+@media (max-width: 900px) {
+  .solution-connector {
+    padding: var(--spacing-sm) 0;
+  }
+  
+  .connector-plus {
+    font-size: 2rem;
+  }
+}
+
+
+/* ============================================
+   SKILLS SECTION
+   ============================================ */
+
+.skills-section {
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.skills-gallery {
+  display: grid;
+  grid-template-columns: 200px 1fr;
+  gap: var(--spacing-xl);
+  align-items: start;
+}
+
+@media (max-width: 968px) {
+  .skills-gallery {
+    grid-template-columns: 1fr;
+    gap: var(--spacing-lg);
+  }
+}
+
+.skills-nav {
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+  position: sticky;
+  top: var(--spacing-lg);
+}
+
+@media (max-width: 968px) {
+  .skills-nav {
+    flex-direction: row;
+    flex-wrap: wrap;
+    gap: var(--spacing-xs);
+    position: static;
+  }
+}
+
+.skill-nav-item {
+  padding: var(--spacing-sm) var(--spacing-md);
+  background: transparent;
+  border: none;
+  border-left: 2px solid transparent;
+  color: var(--color-ash);
+  font-family: var(--font-body);
+  font-size: 0.9375rem;
+  font-weight: 400;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  text-align: left;
+  text-decoration: none;
+  display: block;
+}
+
+.skill-nav-item:hover {
+  color: var(--color-text);
+  background: var(--color-cream);
+}
+
+.skill-nav-item.active {
+  color: var(--color-accent);
+  border-left-color: var(--color-accent);
+  background: var(--color-accent-dim);
+  font-weight: 500;
+}
+
+@media (max-width: 968px) {
+  .skill-nav-item {
+    border-left: none;
+    border-bottom: 2px solid transparent;
+    padding: var(--spacing-xs) var(--spacing-md);
+  }
+  
+  .skill-nav-item.active {
+    border-bottom-color: var(--color-accent);
+  }
+}
+
+.skills-showcase {
+  display: grid;
+  grid-template-columns: 1.2fr 1fr;
+  gap: var(--spacing-lg);
+  align-items: start;
+}
+
+@media (max-width: 1100px) {
+  .skills-showcase {
+    grid-template-columns: 1fr;
+  }
+}
+
+.loading-state {
+  padding: var(--spacing-xl);
+  text-align: center;
+  color: var(--color-ash);
+  font-style: italic;
+}
+
+/* ============================================
+   MOBILE COMMANDS LAYOUT
+   ============================================ */
+
+.mobile-commands-layout {
+  display: none;
+}
+
+@media (max-width: 900px) {
+  .mobile-commands-layout {
+    display: flex;
+    flex-direction: column;
+    gap: var(--spacing-md);
+    /* Without min-width: 0, the flex container expands to fit any intrinsically
+       wide descendant (the split-compare demo blows it out to ~2400px otherwise).
+       The same constraint cascades to direct children via the next rule. */
+    min-width: 0;
+    width: 100%;
+  }
+
+  .mobile-commands-layout > * {
+    min-width: 0;
+    max-width: 100%;
+  }
+
+  .commands-container {
+    display: none;
+  }
+}
+
+.mobile-carousel-wrapper {
+  overflow-x: auto;
+  -webkit-overflow-scrolling: touch;
+  scrollbar-width: none;
+  padding: var(--spacing-xs) 0;
+}
+
+.mobile-carousel-wrapper::-webkit-scrollbar {
+  display: none;
+}
+
+.mobile-carousel {
+  display: flex;
+  gap: var(--spacing-xs);
+  padding-right: var(--spacing-md);
+}
+
+.mobile-cmd-pill {
+  flex-shrink: 0;
+  padding: var(--spacing-sm) var(--spacing-md);
+  min-height: 44px;
+  font-family: var(--font-mono);
+  font-size: 0.8125rem;
+  font-weight: 500;
+  color: var(--color-charcoal);
+  background: none;
+  border: none;
+  border-radius: 100px;
+  cursor: pointer;
+  transition: background 0.2s ease, color 0.2s ease;
+  white-space: nowrap;
+}
+
+.mobile-cmd-pill:hover {
+  background: var(--color-mist);
+}
+
+.mobile-cmd-pill.active {
+  color: var(--color-paper);
+  background: var(--color-ink);
+}
+
+.mobile-demo-area {
+  padding: 0;
+}
+
+.mobile-demo-area .demo-split-comparison {
+  width: 100%;
+}
+
+.mobile-demo-area .split-container {
+  width: 100%;
+  max-width: 100%;
+  height: 320px;
+}
+
+.mobile-demo-area .demo-caption {
+  font-size: 0.75rem;
+  margin-top: var(--spacing-sm);
+}
+
+.mobile-info-area {
+  padding-top: var(--spacing-sm);
+}
+
+.mobile-cmd-info {
+  display: none;
+  padding: var(--spacing-sm) 0;
+}
+
+.mobile-cmd-info.active {
+  display: block;
+}
+
+.mobile-cmd-name {
+  font-family: var(--font-mono);
+  font-size: 1.125rem;
+  font-weight: 600;
+  color: var(--color-ink);
+  margin: 0 0 var(--spacing-xs) 0;
+}
+
+.mobile-cmd-desc {
+  font-size: 0.875rem;
+  color: var(--color-charcoal);
+  line-height: 1.5;
+  margin: 0;
+}
+
+.mobile-cmd-rel {
+  margin-top: var(--spacing-xs);
+  font-size: 0.75rem;
+  color: var(--color-ash);
+}
+
+.mobile-cmd-rel code {
+  font-family: var(--font-mono);
+  color: var(--color-ink);
+}
+
+/* ============================================
+   DOWNLOADS SECTION
+   ============================================ */
+
+.downloads-section {
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.downloads-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+  gap: var(--spacing-lg);
+}
+
+.download-card {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+  padding: var(--spacing-lg);
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  transition: all var(--duration-base) var(--ease-out);
+}
+
+.download-card:hover {
+  border-color: var(--color-accent);
+  transform: translateY(-4px);
+  box-shadow: 0 20px 60px var(--color-accent-dim);
+}
+
+.download-card-icon {
+  margin-bottom: var(--spacing-sm);
+}
+
+.download-card-icon img {
+  width: 40px;
+  height: 40px;
+  object-fit: contain;
+  border-radius: 8px;
+}
+
+.download-card-title {
+  font-family: var(--font-display);
+  font-size: 1.25rem;
+  font-weight: 400;
+  margin: 0 0 var(--spacing-sm) 0;
+}
+
+.download-card-note {
+  font-size: 0.75rem;
+  color: var(--color-ash);
+  margin-bottom: var(--spacing-xs);
+}
+
+.download-card .btn {
+  margin-top: var(--spacing-xs);
+}
+
+/* Install command box */
+.install-command {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-xs);
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+  padding: var(--spacing-sm);
+  margin-top: var(--spacing-sm);
+  width: 100%;
+}
+
+.install-command code {
+  flex: 1;
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  color: var(--color-ink);
+  background: transparent;
+  padding: 0;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.copy-btn {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 28px;
+  height: 28px;
+  background: transparent;
+  border: 1px solid var(--color-mist);
+  border-radius: 4px;
+  color: var(--color-ash);
+  cursor: pointer;
+  flex-shrink: 0;
+  transition: all var(--duration-fast) var(--ease-out);
+}
+
+.copy-btn:hover {
+  background: var(--color-accent-dim);
+  border-color: var(--color-accent);
+  color: var(--color-accent);
+}
+
+.copy-btn.copied {
+  background: var(--color-accent);
+  border-color: var(--color-accent);
+  color: var(--color-paper);
+}
+
+.install-hint {
+  font-size: 0.75rem;
+  color: var(--color-ash);
+  margin: var(--spacing-xs) 0 0 0;
+}
+
+.install-hint code {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  background: var(--color-mist);
+  padding: 2px 5px;
+  border-radius: 3px;
+}
+
+.download-card-details {
+  width: 100%;
+  margin-top: var(--spacing-sm);
+  font-size: 0.8125rem;
+  text-align: left;
+}
+
+.download-card-details summary {
+  cursor: pointer;
+  color: var(--color-ash);
+  font-size: 0.75rem;
+  padding: var(--spacing-xs) 0;
+  list-style: none;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 4px;
+}
+
+.download-card-details summary::before {
+  content: '▶';
+  font-size: 0.5rem;
+  transition: transform var(--duration-fast) var(--ease-out);
+}
+
+.download-card-details[open] summary::before {
+  transform: rotate(90deg);
+}
+
+.download-card-details summary::-webkit-details-marker {
+  display: none;
+}
+
+.download-card-details ol {
+  margin: var(--spacing-sm) 0;
+  padding-left: var(--spacing-md);
+  color: var(--color-charcoal);
+  line-height: 1.6;
+}
+
+.download-card-details li {
+  margin-bottom: 4px;
+}
+
+.download-card-details code {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  background: var(--color-mist);
+  padding: 2px 5px;
+  border-radius: 3px;
+}
+
+.download-card-details a {
+  color: var(--color-accent);
+  text-decoration: none;
+  font-size: 0.75rem;
+}
+
+.download-card-details a:hover {
+  text-decoration: underline;
+}
+
+/* ============================================
+   OPEN SOURCE SECTION
+   ============================================ */
+
+.opensource-section {
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+  text-align: center;
+}
+
+.opensource-content {
+  max-width: 500px;
+  margin: 0 auto;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: var(--spacing-md);
+}
+
+.opensource-title {
+  font-size: clamp(1.5rem, 4vw, 2rem);
+  font-weight: 300;
+}
+
+.opensource-desc {
+  font-size: 1.125rem;
+  color: var(--color-ash);
+  line-height: 1.6;
+}
+
+/* ============================================
+   FOOTER
+   ============================================ */
+
+.site-footer {
+  border-top: 1px solid var(--color-mist);
+  padding: var(--spacing-xl) var(--spacing-lg);
+  background: var(--color-cream);
+}
+
+/* Single horizontal row: wordmark left, nav centered, credit right.
+   Three groups, no two-tier split, no dividers. */
+.footer-row {
+  max-width: var(--width-max);
+  margin: 0 auto;
+  display: grid;
+  grid-template-columns: auto 1fr auto;
+  align-items: center;
+  gap: var(--spacing-xl);
+}
+
+.footer-logo {
+  font-family: var(--font-display);
+  font-size: 1.25rem;
+  font-weight: 400;
+  color: var(--color-ink);
+  text-decoration: none;
+}
+
+.footer-links {
+  display: flex;
+  justify-content: center;
+  gap: var(--spacing-lg);
+  flex-wrap: wrap;
+}
+
+.footer-links a {
+  font-size: 0.875rem;
+  color: var(--color-ash);
+  text-decoration: none;
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+.footer-links a:hover {
+  color: var(--color-accent);
+}
+
+.footer-credit {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-sm);
+  font-size: 0.875rem;
+  color: var(--color-ash);
+  white-space: nowrap;
+}
+
+.footer-credit a {
+  color: var(--color-text);
+  text-decoration: none;
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+.footer-credit a:hover {
+  color: var(--color-accent);
+}
+
+.footer-social-link {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  padding: 4px;
+  color: var(--color-ash);
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+.footer-social-link:hover {
+  color: var(--color-accent);
+}
+
+@media (max-width: 900px) {
+  .footer-row {
+    grid-template-columns: 1fr;
+    justify-items: center;
+    text-align: center;
+    gap: var(--spacing-md);
+  }
+
+  .footer-links {
+    justify-content: center;
+    gap: var(--spacing-sm) var(--spacing-md);
+    font-size: 0.8125rem;
+  }
+
+  .site-footer {
+    padding: var(--spacing-lg) var(--spacing-md);
+  }
+}
+
+/* ============================================
+   BUTTONS
+   ============================================ */
+
+.btn {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  gap: var(--spacing-xs);
+  padding: 1rem 2rem;
+  font-family: var(--font-body);
+  font-size: 0.9375rem;
+  font-weight: 600;
+  letter-spacing: 0.03em;
+  border: none;
+  cursor: pointer;
+  transition: all var(--duration-base) var(--ease-out);
+  position: relative;
+  overflow: hidden;
+  text-decoration: none;
+}
+
+.btn-primary {
+  background: var(--color-ink);
+  color: var(--color-paper);
+}
+
+.btn-primary::before {
+  content: '';
+  position: absolute;
+  inset: 0;
+  background: var(--color-accent);
+  transform: translateY(100%);
+  transition: transform var(--duration-base) var(--ease-out);
+  z-index: 0;
+}
+
+.btn-primary:hover::before {
+  transform: translateY(0);
+}
+
+.btn-primary:hover {
+  color: var(--color-paper);
+}
+
+.btn-primary span,
+.btn-primary svg {
+  position: relative;
+  z-index: 1;
+}
+
+/* For buttons without span wrapper */
+.btn-primary:not(:has(span)) {
+  position: relative;
+  z-index: 1;
+}
+
+.btn-secondary {
+  background: transparent;
+  color: var(--color-ink);
+  border: 1px solid var(--color-ink);
+}
+
+.btn-secondary:hover {
+  background: var(--color-ink);
+  color: var(--color-paper);
+}
+
+/* Focus styles for all buttons */
+.btn:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+
+.btn-primary:focus-visible {
+  outline-color: var(--color-paper);
+  box-shadow: 0 0 0 4px var(--color-accent);
+}
+
+.btn-secondary:focus-visible {
+  outline-color: var(--color-accent);
+}
+
+/* ============================================
+   ANIMATIONS
+   ============================================ */
+
+@keyframes revealUp {
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+@keyframes fadeIn {
+  to {
+    opacity: 1;
+  }
+}
+
+@keyframes float {
+  0%, 100% { transform: translateX(-50%) translateY(0); }
+  50% { transform: translateX(-50%) translateY(-8px); }
+}
+
+@keyframes bounce {
+  0%, 100% { transform: translateY(0); }
+  50% { transform: translateY(4px); }
+}
+
+/* Reveal animation for scroll */
+[data-reveal] {
+  opacity: 0;
+  transform: translateY(30px);
+  transition: opacity 0.8s var(--ease-out), transform 0.8s var(--ease-out);
+}
+
+[data-reveal].revealed {
+  opacity: 1;
+  transform: translateY(0);
+}
+
+/* Staggered reveals */
+[data-reveal]:nth-child(1) { transition-delay: 0s; }
+[data-reveal]:nth-child(2) { transition-delay: 0.1s; }
+[data-reveal]:nth-child(3) { transition-delay: 0.2s; }
+[data-reveal]:nth-child(4) { transition-delay: 0.3s; }
+
+/* ============================================
+   REDUCED MOTION (Accessibility)
+   ============================================ */
+
+@media (prefers-reduced-motion: reduce) {
+  *,
+  *::before,
+  *::after {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+    scroll-behavior: auto !important;
+  }
+
+  /* Allow smooth scrolling to remain for anchor links only if user explicitly triggers */
+  html {
+    scroll-behavior: auto;
+  }
+
+  /* Disable hero canvas animation */
+  .hero-canvas {
+    display: none;
+  }
+
+  /* Disable scroll indicator animation */
+  .hero-scroll-indicator {
+    animation: none;
+    opacity: 1;
+  }
+
+  /* Disable reveal animations - show content immediately */
+  [data-reveal] {
+    opacity: 1;
+    transform: none;
+  }
+
+  /* Disable gallery frame transitions */
+  .gallery-frame {
+    opacity: 1;
+    transform: none;
+  }
+}
+
+/* ============================================
+   ERROR STATES
+   ============================================ */
+
+.load-error {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+  padding: var(--spacing-2xl) var(--spacing-lg);
+  gap: var(--spacing-md);
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  border-radius: 8px;
+}
+
+.load-error-icon {
+  font-size: 2.5rem;
+  color: var(--color-accent);
+}
+
+.load-error-title {
+  font-family: var(--font-display);
+  font-size: 1.5rem;
+  font-weight: 400;
+  color: var(--color-ink);
+  margin: 0;
+}
+
+.load-error-text {
+  font-size: 1rem;
+  color: var(--color-ash);
+  max-width: 40ch;
+  line-height: 1.5;
+}
+
+.load-error-retry {
+  margin-top: var(--spacing-sm);
+}
+
+/* ============================================
+   BIAS TAGS (Compact Problem Section)
+   ============================================ */
+
+.bias-tags {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: var(--spacing-sm);
+  margin-top: var(--spacing-lg);
+}
+
+.bias-tags-label {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 500;
+  letter-spacing: 0.1em;
+  text-transform: uppercase;
+  color: var(--color-ash);
+}
+
+.bias-tags-list {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  gap: var(--spacing-xs);
+}
+
+.bias-tag {
+  font-size: 0.75rem;
+  font-weight: 500;
+  padding: 6px 12px;
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  color: var(--color-charcoal);
+  transition: all var(--duration-fast) var(--ease-out);
+}
+
+.bias-tag:hover {
+  border-color: var(--color-accent);
+  color: var(--color-accent);
+}
+
+/* ============================================
+   ANTIDOTE SECTION
+   ============================================ */
+
+.antidote-section {
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+/* Antidote row: patterns + gallery side by side */
+.antidote-row {
+  display: flex;
+  gap: var(--spacing-xl);
+  align-items: stretch;
+  margin-bottom: var(--spacing-xl);
+}
+
+.antidote-row .patterns-tabbed {
+  flex: 1;
+  min-width: 0;
+  margin-bottom: 0;
+}
+
+/* Gallery of Shame — 3D card stack */
+.gallery-stack-container {
+  flex: 0 0 clamp(320px, 34vw, 420px);
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-xs);
+  position: relative;
+}
+
+
+/* Header: ← Gallery of Shame → */
+.gallery-stack-header {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-xs);
+  white-space: nowrap;
+}
+
+.gallery-stack-title {
+  font-family: var(--font-display);
+  font-size: 1rem;
+  font-weight: 600;
+  color: var(--color-ink);
+  flex: 1;
+}
+
+.gallery-stack-btn {
+  width: 24px;
+  height: 24px;
+  border-radius: 50%;
+  border: 1px solid var(--color-mist);
+  background: white;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 0.6875rem;
+  color: var(--color-charcoal);
+  flex-shrink: 0;
+  transition: border-color var(--duration-fast), background var(--duration-fast);
+  position: relative;
+}
+
+/* Invisible 44px touch target */
+.gallery-stack-btn::after {
+  content: '';
+  position: absolute;
+  inset: -10px;
+}
+
+.gallery-stack-btn:hover {
+  border-color: var(--color-charcoal);
+}
+
+/* Card stack — stacked deck offset to bottom-right */
+.gallery-stack {
+  position: relative;
+  width: calc(100% - 20px);
+  aspect-ratio: 3 / 2;
+  margin-bottom: 20px;
+}
+
+.gallery-stack-card {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  border-radius: 8px;
+  overflow: hidden;
+  border: 1px solid var(--color-mist);
+  text-decoration: none;
+  background: white;
+  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.35s ease, box-shadow 0.35s ease;
+  will-change: transform, opacity;
+}
+
+.gallery-stack-card img {
+  flex: 1;
+  min-height: 0;
+  width: 100%;
+  object-fit: cover;
+  object-position: top left;
+}
+
+.gallery-stack-label {
+  padding: 6px 10px;
+  font-size: 0.75rem;
+  font-weight: 600;
+  color: var(--color-charcoal);
+  flex-shrink: 0;
+}
+
+/* Stack depth: cards peek behind at bottom-right */
+.gallery-stack-card[data-offset="0"] {
+  z-index: 3;
+  transform: translate(0, 0);
+  opacity: 1;
+  box-shadow: 0 2px 12px oklch(0.2 0 0 / 0.1);
+}
+
+.gallery-stack-card[data-offset="1"] {
+  z-index: 2;
+  transform: translate(8px, 8px);
+  opacity: 1;
+  box-shadow: 0 1px 6px oklch(0.2 0 0 / 0.08);
+  pointer-events: none;
+}
+
+.gallery-stack-card[data-offset="2"] {
+  z-index: 1;
+  transform: translate(16px, 16px);
+  opacity: 1;
+  box-shadow: 0 1px 4px oklch(0.2 0 0 / 0.06);
+  pointer-events: none;
+}
+
+.gallery-stack-card:not([data-offset="0"]):not([data-offset="1"]):not([data-offset="2"]) {
+  z-index: 0;
+  transform: translate(20px, 20px);
+  opacity: 0;
+  pointer-events: none;
+}
+
+.gallery-stack-link {
+  font-size: 0.75rem;
+  font-weight: 500;
+  color: var(--color-accent);
+  text-decoration: none;
+  text-align: center;
+}
+
+.gallery-stack-link:hover {
+  text-decoration: underline;
+}
+
+@media (max-width: 900px) {
+  .antidote-row {
+    flex-direction: column;
+  }
+
+  .gallery-stack-container {
+    flex: none;
+    width: 100%;
+    max-width: 360px;
+  }
+
+}
+
+/* ---- Pattern tabs ---- */
+.patterns-tabbed {
+  margin-bottom: var(--spacing-xl);
+  background: white;
+  border-radius: 8px;
+  padding: var(--spacing-lg);
+}
+
+.patterns-tabs {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+  margin-bottom: var(--spacing-md);
+}
+
+@media (max-width: 768px) {
+  .patterns-tabs {
+    flex-wrap: nowrap;
+    overflow-x: auto;
+    scrollbar-width: none;
+    -webkit-overflow-scrolling: touch;
+    margin-left: calc(-1 * var(--spacing-lg));
+    margin-right: calc(-1 * var(--spacing-lg));
+    padding-left: var(--spacing-lg);
+    padding-right: var(--spacing-lg);
+  }
+
+  .patterns-tabs::-webkit-scrollbar {
+    display: none;
+  }
+}
+
+.patterns-tab {
+  font-family: var(--font-body);
+  font-size: 0.75rem;
+  font-weight: 500;
+  color: var(--color-charcoal);
+  background: none;
+  border: none;
+  border-radius: 100px;
+  padding: 6px 12px;
+  cursor: pointer;
+  white-space: nowrap;
+  transition: background 0.2s ease, color 0.2s ease;
+}
+
+.patterns-tab:hover {
+  background: var(--color-mist);
+}
+
+.patterns-tab.is-active {
+  background: var(--color-ink);
+  color: var(--color-paper);
+}
+
+.patterns-content {
+  display: none;
+}
+
+.patterns-content.is-active {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-md);
+}
+
+.patterns-col ul {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+}
+
+.patterns-col li {
+  font-size: 0.8125rem;
+  line-height: 1.55;
+  padding-left: var(--spacing-sm);
+  position: relative;
+  color: var(--color-charcoal);
+  overflow-wrap: break-word;
+  word-break: break-word;
+}
+
+.patterns-col--dont li::before {
+  content: '\00d7';
+  position: absolute;
+  left: 0;
+  color: var(--color-accent);
+  font-weight: 600;
+}
+
+.patterns-col--do li::before {
+  content: '\2713';
+  position: absolute;
+  left: 0;
+  color: var(--color-success, #22c55e);
+  font-weight: 600;
+}
+
+
+/* Detection callout — flat editorial CTA, no card chrome.
+   Image thumbnail on the left, NEW eyebrow + CTA stacked on the right.
+   The whole element is a clickable anchor. Hover shifts color, no
+   transform or shadow theatrics. */
+.detection-callout {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+  text-decoration: none;
+  color: inherit;
+  padding: var(--spacing-sm) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.detection-callout-image {
+  display: block;
+  width: 132px;
+  height: 112px;
+  flex-shrink: 0;
+  object-fit: cover;
+  object-position: top left;
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+  transition: border-color 0.2s ease;
+}
+
+.detection-callout:hover .detection-callout-image {
+  border-color: var(--color-accent);
+}
+
+.detection-callout-inner {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  justify-content: center;
+  gap: 6px;
+}
+
+.detection-eyebrow {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--color-accent);
+}
+
+.detection-callout-title {
+  font-family: var(--font-display);
+  font-size: 1.25rem;
+  font-weight: 600;
+  color: var(--color-ink);
+  letter-spacing: -0.01em;
+  line-height: 1.15;
+}
+
+.detection-callout:hover .detection-callout-title {
+  color: var(--color-accent);
+}
+
+.detection-desc {
+  flex: 1;
+  font-size: 0.8125rem;
+  line-height: 1.55;
+  color: var(--color-ash);
+  min-width: 200px;
+}
+
+.detection-desc code {
+  font-family: var(--font-mono);
+  font-size: 0.8125rem;
+  color: var(--color-charcoal);
+}
+
+.detection-cmd {
+  flex-shrink: 0;
+  font-family: var(--font-body);
+  font-size: 0.875rem;
+  font-weight: 600;
+  color: var(--color-ink);
+  white-space: nowrap;
+  transition: color 0.15s ease;
+}
+
+.detection-callout:hover .detection-cmd {
+  color: var(--color-accent);
+}
+
+/* Antidote footer */
+.antidote-footer {
+  text-align: center;
+}
+
+.antidote-suggest-link {
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+  text-decoration: none;
+}
+
+.antidote-suggest-link:hover {
+  color: var(--color-accent);
+  text-decoration: underline;
+}
+
+/* ============================================
+   SOLUTION SECTION ENHANCEMENTS
+   ============================================ */
+
+/* Main skill item styling */
+.pillar-item--main {
+  background: var(--color-accent-dim);
+  border: 1px solid var(--color-accent);
+}
+
+.pillar-item--main .pillar-item-name {
+  font-size: 1.125rem;
+  font-weight: 600;
+  color: var(--color-accent);
+}
+
+.pillar-item--ref {
+  background: transparent;
+  padding: var(--spacing-xs) var(--spacing-sm);
+}
+
+.pillar-item-label {
+  font-size: 0.75rem;
+  font-weight: 500;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  color: var(--color-ash);
+}
+
+/* Reference domain tags */
+.pillar-refs {
+  display: flex;
+  flex-wrap: wrap;
+  gap: var(--spacing-xs);
+  padding: 0 var(--spacing-sm);
+}
+
+.pillar-ref {
+  font-size: 0.6875rem;
+  font-weight: 500;
+  text-transform: uppercase;
+  letter-spacing: 0.03em;
+  padding: 4px 10px;
+  background: var(--color-paper);
+  color: var(--color-ash);
+  border: 1px solid var(--color-mist);
+  border-radius: 3px;
+  transition: all var(--duration-fast) var(--ease-out);
+}
+
+.pillar-ref:hover {
+  border-color: var(--color-accent);
+  color: var(--color-accent);
+}
+
+/* Command groups */
+.pillar-command-group {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: var(--spacing-xs);
+  padding: var(--spacing-sm);
+  background: var(--color-paper);
+  border-radius: 4px;
+}
+
+.pillar-group-label {
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  color: var(--color-ash);
+  width: 100%;
+  margin-bottom: 4px;
+}
+
+.pillar-command-group .pillar-item-code {
+  font-size: 0.8125rem;
+  padding: 4px 8px;
+  background: var(--color-accent-dim);
+  border-radius: 3px;
+}
+
+/* ============================================
+   PLATFORMS SECTION (alias for downloads)
+   ============================================ */
+
+.platforms-section {
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+@media (min-width: 1100px) {
+  .platforms-section {
+    border-top: none;
+  }
+}
+
+.platforms-section .section-subtitle {
+  max-width: 60ch;
+}
+
+/* Two-path install layout */
+/* Row 1: full-width primary install card with internal 2-column split */
+.install-row-primary {
+  display: grid;
+  grid-template-columns: 1.1fr 0.9fr;
+  gap: var(--spacing-xl);
+  align-items: start;
+  margin: 0 0 var(--spacing-xl);
+  background: white;
+  border-radius: 16px;
+  padding: var(--spacing-lg);
+  min-width: 0;
+}
+
+.install-primary-main {
+  display: flex;
+  flex-direction: column;
+  min-width: 0;
+}
+
+.install-primary-alts {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-lg);
+  min-width: 0;
+  padding-left: var(--spacing-xl);
+  border-left: 1px solid var(--color-mist);
+}
+
+.install-alts-label {
+  display: block;
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.14em;
+  color: var(--color-ash);
+}
+
+.install-alt-sublabel {
+  font-family: var(--font-body);
+  font-size: 0.75rem;
+  font-weight: 400;
+  color: var(--color-ash);
+  text-transform: none;
+  letter-spacing: 0;
+  margin-left: 4px;
+}
+
+/* Compact zip download links — replaces the old big black download button.
+   Two equal-weight options stacked vertically, each is its own affordance. */
+.install-zip-links {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  margin-top: var(--spacing-xs);
+}
+
+.install-zip-link {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-sm);
+  padding: 8px 12px;
+  font-family: var(--font-body);
+  font-size: 0.8125rem;
+  color: var(--color-charcoal);
+  background: transparent;
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+  cursor: pointer;
+  text-align: left;
+  transition: border-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
+}
+
+.install-zip-link:hover {
+  border-color: var(--color-accent);
+  color: var(--color-accent);
+}
+
+.install-zip-link svg {
+  flex-shrink: 0;
+  color: var(--color-ash);
+}
+
+.install-zip-link:hover svg {
+  color: var(--color-accent);
+}
+
+.install-zip-link-name {
+  font-weight: 600;
+  color: var(--color-ink);
+  white-space: nowrap;
+}
+
+.install-zip-link:hover .install-zip-link-name {
+  color: var(--color-accent);
+}
+
+.install-zip-link-examples {
+  display: flex;
+  flex-wrap: nowrap;
+  gap: 4px;
+  margin-left: auto;
+}
+
+.install-alt-method .install-zip-link-examples code {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  background: var(--color-accent-dim);
+  color: var(--color-accent);
+  padding: 2px 6px;
+  border-radius: 3px;
+  white-space: nowrap;
+}
+
+.install-path {
+  display: flex;
+  flex-direction: column;
+  min-width: 0;
+}
+
+.install-path-primary {
+  background: white;
+  border-radius: 16px;
+  padding: var(--spacing-lg);
+}
+
+/* Steps 2-4: vertical accordion. Native <details name="install-steps">
+   gives mutual exclusion (opening one closes the others). Items are
+   separated by hairlines, not card chrome. */
+.install-accordion {
+  display: flex;
+  flex-direction: column;
+  margin: 0 0 var(--spacing-xl);
+}
+
+.install-step {
+  border-top: 1px solid var(--color-mist);
+}
+
+.install-step:last-child {
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.install-step-summary {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+  padding: var(--spacing-md) var(--spacing-xs);
+  cursor: pointer;
+  list-style: none;
+  transition: background var(--duration-fast) var(--ease-out);
+}
+
+.install-step-summary::-webkit-details-marker {
+  display: none;
+}
+
+.install-step-summary:hover {
+  background: var(--color-cream);
+}
+
+.install-step-summary h3 {
+  flex: 1;
+  margin: 0;
+}
+
+.install-step-arrow {
+  flex-shrink: 0;
+  color: var(--color-ash);
+  transition: transform var(--duration-base) var(--ease-out);
+}
+
+.install-step[open] .install-step-arrow {
+  transform: rotate(90deg);
+}
+
+.install-step-body {
+  padding: 0 var(--spacing-xs) var(--spacing-md);
+  display: flex;
+  flex-direction: column;
+  max-width: 56ch;
+}
+
+.install-step-body .install-path-desc {
+  margin-top: 0;
+}
+
+.install-step-status {
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+  margin: 0 0 var(--spacing-sm);
+  font-style: italic;
+}
+
+.install-path-title {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 10px;
+  font-family: var(--font-display);
+  font-size: 1.5rem;
+  font-weight: 600;
+  margin: 0 0 var(--spacing-sm);
+  line-height: 1.2;
+}
+
+/* titles unified - no size override for step 3 */
+
+.install-path-badge {
+  font-family: var(--font-mono);
+  font-size: 0.5rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--color-accent);
+  border: 1px solid var(--color-accent);
+  border-radius: 3px;
+  padding: 2px 6px;
+  vertical-align: middle;
+  margin-left: 6px;
+  position: relative;
+  top: -2px;
+}
+
+.install-path-badge-muted {
+  color: var(--color-ash);
+  border-color: var(--color-mist);
+}
+
+.install-path-desc {
+  font-size: 0.9375rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  margin: 0 0 var(--spacing-md);
+}
+
+.terminal-header-license {
+  margin-left: auto;
+  font-size: 0.6875rem;
+  color: oklch(65% 0.01 350);
+  letter-spacing: 0.01em;
+}
+
+.terminal-header-license a {
+  color: inherit;
+  text-decoration: underline;
+  text-decoration-color: oklch(85% 0.005 350);
+  text-underline-offset: 2px;
+}
+
+.terminal-header-license a:hover {
+  color: var(--color-charcoal);
+}
+
+/* removed - unified font sizes across columns */
+
+.install-path-terminal {
+  margin-bottom: var(--spacing-sm);
+}
+
+.install-path-terminal .glass-terminal {
+  height: auto;
+}
+
+.install-path-terminal .terminal-body {
+  padding: 0;
+}
+
+.install-path-next {
+  font-size: 0.8125rem;
+  color: var(--color-charcoal);
+  line-height: 1.5;
+  margin-top: auto;
+  padding-top: var(--spacing-md);
+}
+
+.install-path-next code {
+  font-family: var(--font-mono);
+  font-size: 0.8125rem;
+  font-weight: 600;
+  color: var(--color-ink);
+}
+
+.install-path-slash {
+  color: var(--color-accent);
+}
+
+.install-path-subcommands {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  margin-top: var(--spacing-sm);
+}
+
+.install-path-subcommand {
+  font-size: 0.75rem;
+  color: var(--color-charcoal);
+  line-height: 1.4;
+}
+
+.install-path-subcommand code {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  color: var(--color-ink);
+  background: var(--color-mist);
+  padding: 2px 6px;
+  border-radius: 3px;
+  margin-right: 4px;
+}
+
+.install-path-link {
+  margin-top: auto;
+  padding-top: var(--spacing-md);
+  font-size: 0.8125rem;
+}
+
+.install-path-link a {
+  color: var(--color-accent);
+  text-decoration: none;
+}
+
+.install-path-link a:hover {
+  text-decoration: underline;
+}
+
+/* Lightweight terminal block — used in row 2 (CLI) where the heavy
+   skeuomorphic glass-terminal would be visually too much. Just a thin
+   bordered code line with a copy button and an optional note below. */
+.install-cmd-block {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-xs);
+  margin-bottom: var(--spacing-sm);
+}
+
+.install-cmd-line {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-sm);
+  padding: 10px 14px;
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+  min-width: 0;
+}
+
+.install-cmd-line .terminal-prompt {
+  flex-shrink: 0;
+  color: var(--color-accent);
+  font-family: var(--font-mono);
+  font-size: 0.8125rem;
+}
+
+.install-cmd-line code {
+  flex: 1;
+  font-family: var(--font-mono);
+  font-size: 0.8125rem;
+  color: var(--color-ink);
+  background: transparent;
+  padding: 0;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.install-cmd-line .copy-btn {
+  flex-shrink: 0;
+}
+
+.install-cmd-note {
+  font-size: 0.75rem;
+  color: var(--color-ash);
+  line-height: 1.5;
+}
+
+.install-cmd-note code {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  background: var(--color-mist);
+  padding: 2px 5px;
+  border-radius: 3px;
+  color: var(--color-ink);
+}
+
+
+/* Better together callout */
+.install-together {
+  max-width: 960px;
+  margin: 0 0 var(--spacing-xl);
+  border: 1px solid var(--color-mist);
+  border-radius: 8px;
+  padding: var(--spacing-sm) var(--spacing-md);
+}
+
+.install-together-inner {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+}
+
+.install-together-badge {
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  color: var(--color-accent);
+  border: 1px solid var(--color-accent);
+  padding: 2px 10px;
+  border-radius: 99px;
+  flex-shrink: 0;
+  white-space: nowrap;
+}
+
+.install-together-text {
+  font-size: 0.8125rem;
+  line-height: 1.55;
+  color: var(--color-ash);
+  margin: 0;
+}
+
+/* Inline step numbers — sit to the left of the title text inside each h3.
+   The h3 uses flex with align-items: center so the step is centered with
+   the text without needing vertical-align fiddling. Margin/gap is handled
+   by the parent flex's gap. */
+.install-path-step {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 22px;
+  height: 22px;
+  border-radius: 50%;
+  border: 1.5px solid var(--color-accent);
+  color: var(--color-accent);
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 700;
+  line-height: 1;
+  flex-shrink: 0;
+}
+
+/* Stay updated (third column) */
+.install-updated-subscribe {
+  display: grid;
+  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
+  gap: var(--spacing-sm);
+  margin-bottom: var(--spacing-md);
+  align-items: stretch;
+}
+
+.install-updated-substack {
+  display: block;
+  width: 100%;
+  min-width: 0;
+  height: 130px;
+  border: 1px solid var(--color-mist);
+  border-radius: 8px;
+  background: var(--color-paper);
+}
+
+.install-updated-x {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  justify-content: space-between;
+  gap: var(--spacing-xs);
+  height: 130px;
+  padding: var(--spacing-sm) var(--spacing-md);
+  border: 1px solid var(--color-mist);
+  border-radius: 8px;
+  background: var(--color-paper);
+  color: var(--color-ink);
+  text-decoration: none;
+  transition: border-color var(--duration-fast) var(--ease-out),
+              background var(--duration-fast) var(--ease-out),
+              transform var(--duration-fast) var(--ease-out);
+}
+
+.install-updated-x:hover {
+  border-color: var(--color-ink);
+  background: var(--color-cream);
+}
+
+.install-updated-x-icon {
+  color: var(--color-ink);
+  flex-shrink: 0;
+}
+
+.install-updated-x-label {
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--color-ash);
+  font-family: var(--font-mono);
+}
+
+.install-updated-x-handle {
+  font-size: 1rem;
+  font-weight: 600;
+  color: var(--color-ink);
+  letter-spacing: -0.01em;
+}
+
+.install-updated-x:hover .install-updated-x-handle {
+  color: var(--color-accent);
+}
+
+.install-updated-refs {
+  margin-top: auto;
+  padding-top: var(--spacing-md);
+  border-top: 1px solid var(--color-mist);
+}
+
+.install-updated-refs-label {
+  display: block;
+  font-family: var(--font-mono);
+  font-size: 0.5625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  color: var(--color-ash);
+  margin-bottom: 8px;
+}
+
+.install-updated-ref {
+  display: block;
+  font-size: 0.8125rem;
+  color: var(--color-charcoal);
+  text-decoration: none;
+  margin-bottom: 6px;
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+a.install-updated-ref:hover {
+  color: var(--color-accent);
+}
+
+.install-updated-ref-terminal {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-xs);
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  color: var(--color-charcoal);
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+  padding: 6px 10px;
+  margin-top: 8px;
+}
+
+.install-updated-ref-terminal .terminal-prompt {
+  color: var(--color-ash);
+  font-size: 0.75rem;
+}
+
+.install-updated-ref-terminal code {
+  font-family: inherit;
+  font-size: inherit;
+  background: none;
+  padding: 0;
+  color: var(--color-ink);
+}
+
+@media (max-width: 900px) {
+  .install-row-primary {
+    grid-template-columns: 1fr;
+    gap: var(--spacing-lg);
+    max-width: calc(100vw - var(--spacing-md) * 2);
+  }
+
+  .install-primary-alts {
+    padding-left: 0;
+    padding-top: var(--spacing-lg);
+    border-left: none;
+    border-top: 1px solid var(--color-mist);
+  }
+
+  .install-path-primary {
+    margin-bottom: var(--spacing-sm);
+    padding: var(--spacing-md);
+  }
+
+  .install-path-title {
+    font-size: 1.25rem;
+  }
+
+  .install-path-desc {
+    font-size: 0.875rem;
+    overflow-wrap: break-word;
+  }
+
+  .install-together-inner {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: var(--spacing-xs);
+  }
+
+  .install-updated-subscribe {
+    grid-template-columns: 1fr;
+  }
+
+  .install-updated-substack {
+    height: 130px;
+  }
+
+  .install-updated-x {
+    height: auto;
+    flex-direction: row;
+    align-items: center;
+    justify-content: flex-start;
+    gap: var(--spacing-sm);
+    padding: var(--spacing-sm) var(--spacing-md);
+  }
+
+  .install-updated-x-label {
+    display: none;
+  }
+
+  .install-terminal-cmd code {
+    font-size: 0.75rem;
+    white-space: normal;
+    overflow-wrap: break-word;
+  }
+
+  .install-alt-method {
+    padding: var(--spacing-sm);
+  }
+
+  .install-alt-method .install-terminal-cmd code {
+    font-size: 0.6875rem;
+    white-space: normal;
+    overflow-wrap: break-word;
+  }
+
+  .install-alt-method .install-terminal-cmd .copy-btn {
+    display: none;
+  }
+
+  .install-path-terminal .glass-terminal {
+    box-shadow: none;
+  }
+
+  .install-terminal {
+    max-width: 100%;
+  }
+
+  .install-alternatives {
+    max-width: 100%;
+  }
+}
+
+/* Install terminal (glass terminal style) */
+.install-terminal {
+  max-width: 640px;
+  margin: 0 auto;
+}
+
+.install-terminal .glass-terminal {
+  height: auto;
+}
+
+.install-terminal .terminal-body {
+  padding: 0;
+  display: flex;
+  flex-direction: column;
+}
+
+.install-terminal-row {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  padding: var(--spacing-md) var(--spacing-lg);
+}
+
+.install-terminal-label {
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  color: var(--color-ash);
+}
+
+.install-terminal-cmd {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-sm);
+}
+
+.install-terminal-cmd .terminal-prompt {
+  flex-shrink: 0;
+}
+
+.install-terminal-cmd code {
+  flex: 1;
+  font-family: var(--font-mono);
+  font-size: 0.9375rem;
+  color: var(--color-ink);
+  background: transparent;
+  padding: 0;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.install-terminal-cmd .copy-btn {
+  flex-shrink: 0;
+}
+
+.install-terminal-cmd .btn {
+  padding: 0.5rem 1rem;
+  font-size: 0.8125rem;
+}
+
+.install-terminal-note {
+  font-size: 0.75rem;
+  color: var(--color-ash);
+  padding-left: calc(0.75rem + var(--spacing-sm)); /* align with code after prompt */
+}
+
+.install-terminal-note code {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  background: var(--color-mist);
+  padding: 2px 5px;
+  border-radius: 3px;
+  color: var(--color-ink);
+}
+
+.install-terminal-divider {
+  height: 1px;
+  background: var(--color-mist);
+  margin: 0;
+}
+
+@media (max-width: 600px) {
+  .install-terminal-row {
+    padding: var(--spacing-sm) var(--spacing-md);
+  }
+
+  .install-terminal-cmd code {
+    font-size: 0.75rem;
+  }
+}
+
+/* Works with provider badges */
+.install-providers {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: var(--spacing-sm);
+  margin-top: var(--spacing-md);
+}
+
+.install-providers-label {
+  font-size: 0.75rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  color: var(--color-ash);
+}
+
+.install-providers-row {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  gap: var(--spacing-sm);
+}
+
+.install-provider-badge {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  font-size: 0.8125rem;
+  color: var(--color-charcoal);
+}
+
+.install-provider-badge img {
+  border-radius: 4px;
+}
+
+/* Install alternatives (collapsible) */
+.install-alternatives {
+  max-width: 640px;
+  margin: var(--spacing-sm) auto 0;
+}
+
+.install-alternatives-toggle {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--color-ash);
+  cursor: pointer;
+  padding: var(--spacing-sm) 0;
+  list-style: none;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+.install-alternatives-toggle::-webkit-details-marker {
+  display: none;
+}
+
+.install-alternatives-toggle::before {
+  content: '';
+  display: inline-block;
+  width: 0;
+  height: 0;
+  border-left: 5px solid currentColor;
+  border-top: 4px solid transparent;
+  border-bottom: 4px solid transparent;
+  transition: transform var(--duration-fast) var(--ease-out);
+}
+
+.install-alternatives[open] .install-alternatives-toggle::before {
+  transform: rotate(90deg);
+}
+
+.install-alternatives-toggle:hover {
+  color: var(--color-charcoal);
+}
+
+.install-alternatives-content {
+  animation: altFadeIn 0.2s var(--ease-out);
+}
+
+@keyframes altFadeIn {
+  from { opacity: 0; transform: translateY(-4px); }
+  to { opacity: 1; transform: translateY(0); }
+}
+
+.install-alt-method {
+  padding: var(--spacing-md) var(--spacing-lg);
+}
+
+.install-alt-label {
+  display: block;
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  color: var(--color-ash);
+  margin-bottom: 6px;
+}
+
+.install-alt-method .install-terminal-cmd {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-sm);
+}
+
+.install-alt-method .terminal-prompt {
+  color: var(--color-accent);
+  font-family: var(--font-mono);
+  font-weight: bold;
+  user-select: none;
+}
+
+.install-alt-method code {
+  font-family: var(--font-mono);
+  font-size: 0.875rem;
+  color: var(--color-ink);
+}
+
+.install-alt-note {
+  display: block;
+  font-size: 0.75rem;
+  color: var(--color-ash);
+  margin-top: 4px;
+}
+
+.install-alt-note code {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  background: var(--color-mist);
+  padding: 2px 5px;
+  border-radius: 3px;
+  color: var(--color-ink);
+}
+
+/* Visually hidden helper (kept for accessibility patterns elsewhere) */
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border: 0;
+}
+
+@media (max-width: 600px) {
+  .install-alt-method code {
+    font-size: 0.75rem;
+  }
+}
+
+/* Tooltips */
+.has-tooltip {
+  position: relative;
+  cursor: default;
+}
+
+.has-tooltip::after {
+  content: attr(data-tooltip);
+  position: absolute;
+  bottom: calc(100% + 8px);
+  left: 50%;
+  transform: translateX(-50%);
+  padding: 6px 10px;
+  background: var(--color-ink);
+  color: var(--color-paper);
+  font-size: 0.6875rem;
+  line-height: 1.4;
+  border-radius: 6px;
+  white-space: nowrap;
+  pointer-events: none;
+  opacity: 0;
+  transition: opacity 0.15s ease;
+  z-index: 100;
+}
+
+.has-tooltip:hover::after {
+  opacity: 1;
+}
+
+/* Hero logo icon wrapper */
+.hero-logo-icon {
+  display: inline-flex;
+  align-items: center;
+}
+
+.download-tip {
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+  margin-top: var(--spacing-sm);
+  text-align: center;
+}
+
+.download-tip a {
+  color: var(--color-accent);
+  text-decoration: none;
+}
+
+.download-tip a:hover {
+  text-decoration: underline;
+}
+
+
+/* ============================================
+   CONSULTING SECTION
+   ============================================ */
+
+.consulting-section {
+  padding: var(--spacing-xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.consulting-content {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: var(--spacing-lg);
+  flex-wrap: wrap;
+}
+
+.consulting-actions {
+  display: flex;
+  gap: var(--spacing-sm);
+  flex-shrink: 0;
+}
+
+.consulting-text {
+  flex: 1;
+  min-width: 280px;
+}
+
+.consulting-title {
+  font-size: clamp(1.5rem, 4vw, 2rem);
+  font-weight: 300;
+  font-style: italic;
+  margin: 0 0 var(--spacing-sm) 0;
+}
+
+.consulting-desc {
+  font-size: 1rem;
+  color: var(--color-charcoal);
+  line-height: 1.6;
+  margin: 0;
+  max-width: 45ch;
+}
+
+@media (max-width: 600px) {
+  .consulting-content {
+    flex-direction: column;
+    align-items: flex-start;
+  }
+
+  .consulting-actions {
+    flex-direction: column;
+    width: 100%;
+  }
+
+  .consulting-actions .btn {
+    width: 100%;
+    justify-content: center;
+  }
+}

public/css/problem-section.css 🔗

@@ -0,0 +1,121 @@
+/**
+ * Problem Section - Split Screen Comparison
+ * Interactive before/after comparison with angled divider
+ */
+
+/* Subtle grid background for the container */
+.split-container::before {
+  content: '';
+  position: absolute;
+  inset: 0;
+  background-image:
+    linear-gradient(var(--color-mist) 1px, transparent 1px),
+    linear-gradient(90deg, var(--color-mist) 1px, transparent 1px);
+  background-size: 20px 20px;
+  opacity: 0.3;
+  pointer-events: none;
+}
+
+/* Hover hint */
+.split-container::after {
+  content: '← Drag →';
+  position: absolute;
+  bottom: 12px;
+  left: 50%;
+  transform: translateX(-50%);
+  font-size: 0.625rem;
+  font-weight: 600;
+  letter-spacing: 0.1em;
+  text-transform: uppercase;
+  color: var(--color-ash);
+  background: var(--color-paper);
+  padding: 4px 12px;
+  border-radius: 4px;
+  opacity: 0.8;
+  transition: opacity 0.3s ease;
+  z-index: 10;
+}
+
+.split-container:hover::after {
+  opacity: 0;
+}
+
+/* Hover state for impeccable card */
+.split-after .impeccable-card {
+  box-shadow: 0 10px 40px rgba(0,0,0,0.08);
+}
+
+/* Entry animation for the divider */
+@keyframes splitEntry {
+  from {
+    opacity: 0;
+    transform: translateX(-50%) skewX(-10deg) scaleY(0.8);
+  }
+  to {
+    opacity: 1;
+    transform: translateX(-50%) skewX(-10deg) scaleY(1);
+  }
+}
+
+.split-divider {
+  animation: splitEntry 0.6s var(--ease-out) 0.3s backwards;
+}
+
+/* Labels hover states */
+.split-label-item {
+  transition: color var(--duration-fast) var(--ease-out);
+  cursor: default;
+}
+
+.split-label-item:hover {
+  color: var(--color-text);
+}
+
+.split-label-item[data-point="after"]:hover .split-label-dot--accent {
+  transform: scale(1.3);
+}
+
+.split-label-dot {
+  transition: transform var(--duration-fast) var(--ease-spring);
+}
+
+/* Floating corner badges */
+.split-badge {
+  position: absolute;
+  top: 10px;
+  font-size: 0.625rem;
+  font-weight: 600;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  padding: 3px 8px;
+  border-radius: 3px;
+  z-index: 5;
+  pointer-events: none;
+}
+
+.split-badge--before {
+  left: 10px;
+  color: var(--color-ash);
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+}
+
+.split-badge--after {
+  right: 10px;
+  color: var(--color-paper);
+  background: var(--color-accent);
+}
+
+/* Mobile adjustments */
+@media (hover: none) {
+  .split-container::after {
+    content: '← Swipe →';
+  }
+}
+
+@media (max-width: 600px) {
+  .split-label {
+    font-size: 0.5625rem;
+    padding: 4px 10px;
+  }
+}

public/css/skill-demos.css 🔗

@@ -0,0 +1,962 @@
+/**
+ * Skill Demos - Interactive before/after demonstrations
+ * Renaissance Edition
+ */
+
+/* ============================================
+   TABBED CONTAINER
+   ============================================ */
+
+.demo-tabbed-container {
+  display: flex;
+  flex-direction: column;
+}
+
+.demo-tabs {
+  display: flex;
+  gap: 0;
+  margin-bottom: 0;
+  justify-content: center;
+  background: var(--color-paper);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.demo-tab {
+  padding: var(--spacing-sm) var(--spacing-lg);
+  background: transparent;
+  border: none;
+  border-bottom: 2px solid transparent;
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  font-weight: 500;
+  letter-spacing: 0.05em;
+  text-transform: uppercase;
+  color: var(--color-ash);
+  cursor: pointer;
+  transition: all var(--duration-fast) var(--ease-out);
+}
+
+.demo-tab:hover {
+  color: var(--color-text);
+  background: var(--color-cream);
+}
+
+.demo-tab.active {
+  color: var(--color-accent);
+  border-bottom-color: var(--color-accent);
+  background: var(--color-accent-dim);
+}
+
+.demo-panels {
+  flex: 1;
+}
+
+.demo-panel {
+  display: none;
+}
+
+.demo-panel.active {
+  display: block;
+  animation: fadeSlideIn 0.3s var(--ease-out);
+}
+
+@keyframes fadeSlideIn {
+  from {
+    opacity: 0;
+    transform: translateY(10px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+/* ============================================
+   DEMO CONTAINER
+   ============================================ */
+
+.demo-container {
+  background: var(--color-paper);
+  border: none;
+  border-radius: 0;
+  overflow: hidden;
+}
+
+.demo-header {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: var(--spacing-sm) var(--spacing-md);
+  background: var(--color-paper);
+  border-bottom: 1px solid var(--color-mist);
+  min-height: 48px;
+}
+
+.demo-toggle {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+}
+
+.demo-toggle-label {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--color-ash);
+  transition: color var(--duration-fast) var(--ease-out);
+  cursor: pointer;
+}
+
+.demo-toggle-label:hover {
+  color: var(--color-text);
+}
+
+.demo-toggle-label.active {
+  color: var(--color-accent);
+}
+
+.demo-toggle-switch {
+  position: relative;
+  width: 44px;
+  height: 24px;
+  background: var(--color-mist);
+  border-radius: 12px;
+  cursor: pointer;
+  transition: background var(--duration-fast) var(--ease-out);
+  border: 1px solid transparent;
+  padding: 0;
+  font: inherit;
+}
+
+.demo-toggle-switch:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+
+.demo-toggle-switch:hover {
+  border-color: var(--color-ash);
+}
+
+.demo-toggle-switch::after {
+  content: '';
+  position: absolute;
+  top: 3px;
+  left: 3px;
+  width: 16px;
+  height: 16px;
+  background: var(--color-paper);
+  border-radius: 50%;
+  box-shadow: 0 1px 4px rgba(0,0,0,0.15);
+  transition: transform var(--duration-base) var(--ease-spring);
+}
+
+.demo-toggle-switch.active {
+  background: var(--color-accent);
+}
+
+.demo-toggle-switch.active::after {
+  transform: translateX(20px);
+}
+
+.demo-viewport {
+  padding: var(--spacing-xl);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  min-height: 280px;
+  background: var(--color-cream);
+  transition: background var(--duration-base) var(--ease-out);
+}
+
+.demo-viewport[data-state="after"] {
+  background: var(--color-paper);
+}
+
+.demo-caption {
+  padding: var(--spacing-sm) var(--spacing-md);
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  letter-spacing: 0.03em;
+  color: var(--color-ash);
+  text-align: center;
+}
+
+/* ============================================
+   UX WRITING DEMOS
+   ============================================ */
+
+.uxw-demo {
+  width: 100%;
+  max-width: 320px;
+  padding: var(--spacing-lg);
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+  text-align: center;
+}
+
+/* Error Messages */
+.uxw-error-icon { font-size: 2rem; margin-bottom: var(--spacing-sm); }
+.uxw-error-title { font-weight: 600; color: #c00; margin-bottom: var(--spacing-xs); }
+.uxw-error-text { font-size: 0.875rem; color: var(--color-ash); }
+.uxw-error-action { margin-top: var(--spacing-sm); font-size: 0.875rem; color: var(--color-accent); cursor: pointer; text-decoration: underline; }
+
+.uxw-error-after .uxw-error-icon { color: var(--color-accent); }
+.uxw-error-after .uxw-error-title { color: var(--color-text); }
+.uxw-error-after .uxw-error-text { color: var(--color-charcoal); }
+
+/* Button Labels */
+.uxw-button-context { font-size: 0.875rem; color: var(--color-charcoal); margin-bottom: var(--spacing-md); font-weight: 500; }
+.uxw-button-row { display: flex; gap: var(--spacing-sm); justify-content: center; }
+.uxw-btn { padding: var(--spacing-xs) var(--spacing-md); border-radius: 4px; font-size: 0.875rem; font-weight: 500; cursor: pointer; border: none; }
+.uxw-btn-primary { background: var(--color-text); color: var(--color-paper); }
+.uxw-btn-secondary { background: transparent; color: var(--color-ash); border: 1px solid var(--color-mist); }
+.uxw-btn-danger { background: #c00; color: white; }
+
+/* Empty States */
+.uxw-empty-icon { font-size: 2.5rem; margin-bottom: var(--spacing-sm); opacity: 0.4; }
+.uxw-empty-title { font-weight: 500; color: var(--color-ash); }
+.uxw-empty-text { font-size: 0.875rem; color: var(--color-charcoal); margin-top: var(--spacing-xs); }
+.uxw-empty-action { margin-top: var(--spacing-md); }
+
+.uxw-empty-after .uxw-empty-icon { opacity: 1; }
+.uxw-empty-after .uxw-empty-title { color: var(--color-text); }
+
+/* ============================================
+   SPATIAL DESIGN DEMOS
+   ============================================ */
+
+.spatial-demo {
+  width: 100%;
+  max-width: 340px;
+  padding: var(--spacing-md);
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+}
+
+/* Grid Systems */
+.spatial-grid-before {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+}
+
+.spatial-grid-after {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: var(--spacing-sm);
+}
+
+.spatial-card-item {
+  padding: var(--spacing-sm);
+  background: var(--color-bg);
+  border: 1px solid var(--color-mist);
+  border-radius: 4px;
+  font-size: 0.8125rem;
+  color: var(--color-charcoal);
+  text-align: center;
+}
+
+.spatial-grid-after .spatial-card-item {
+  width: auto !important;
+}
+
+/* Visual Hierarchy */
+.spatial-hierarchy-before .spatial-h-title,
+.spatial-hierarchy-before .spatial-h-subtitle,
+.spatial-hierarchy-before .spatial-h-cta,
+.spatial-hierarchy-before .spatial-h-link {
+  font-size: 0.9375rem;
+  margin-bottom: var(--spacing-xs);
+  color: var(--color-charcoal);
+}
+
+.spatial-hierarchy-after .spatial-h-title {
+  font-family: var(--font-display);
+  font-size: 1.75rem;
+  font-weight: 300;
+  font-style: italic;
+  margin-bottom: var(--spacing-xs);
+  color: var(--color-text);
+}
+
+.spatial-hierarchy-after .spatial-h-subtitle {
+  font-size: 0.6875rem;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  color: var(--color-ash);
+  margin-bottom: var(--spacing-md);
+}
+
+.spatial-hierarchy-after .spatial-h-cta {
+  display: inline-block;
+  padding: var(--spacing-sm) var(--spacing-lg);
+  background: var(--color-text);
+  color: var(--color-paper);
+  font-size: 0.875rem;
+  font-weight: 500;
+  border-radius: 4px;
+  margin-bottom: var(--spacing-sm);
+}
+
+.spatial-hierarchy-after .spatial-h-link {
+  font-size: 0.75rem;
+  color: var(--color-ash);
+}
+
+/* Whitespace */
+.spatial-whitespace-before {
+  padding: var(--spacing-xs) !important;
+}
+
+.spatial-whitespace-before .spatial-ws-title { font-size: 1rem; font-weight: 600; margin-bottom: 2px; }
+.spatial-whitespace-before .spatial-ws-price { font-size: 0.875rem; color: var(--color-ash); margin-bottom: 4px; }
+.spatial-whitespace-before .spatial-ws-features { font-size: 0.75rem; color: var(--color-ash); margin-bottom: 6px; }
+.spatial-whitespace-before .spatial-ws-btn { width: 100%; padding: 6px; font-size: 0.75rem; background: var(--color-text); color: var(--color-paper); border: none; border-radius: 3px; cursor: pointer; }
+
+.spatial-whitespace-after {
+  padding: var(--spacing-lg) !important;
+}
+
+.spatial-whitespace-after .spatial-ws-title { font-family: var(--font-display); font-size: 1.5rem; font-weight: 400; margin-bottom: var(--spacing-sm); }
+.spatial-whitespace-after .spatial-ws-price { font-size: 1.25rem; font-weight: 600; color: var(--color-text); margin-bottom: var(--spacing-sm); }
+.spatial-whitespace-after .spatial-ws-features { font-size: 0.8125rem; color: var(--color-ash); margin-bottom: var(--spacing-lg); line-height: 1.6; }
+.spatial-whitespace-after .spatial-ws-btn { width: 100%; padding: var(--spacing-sm); font-size: 0.875rem; background: var(--color-text); color: var(--color-paper); border: none; border-radius: 4px; cursor: pointer; font-weight: 500; }
+
+/* ============================================
+   MOTION DESIGN DEMOS
+   ============================================ */
+
+.motion-demo {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: var(--spacing-sm);
+  width: 100%;
+  max-width: 280px;
+}
+
+/* Staggered Reveal */
+.motion-stagger-demo {
+  align-items: stretch;
+}
+
+.motion-list-item {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-sm);
+  padding: var(--spacing-sm) var(--spacing-md);
+  background: var(--color-bg);
+  border: 1px solid var(--color-mist);
+  border-radius: 4px;
+  font-size: 0.875rem;
+  color: var(--color-charcoal);
+}
+
+.motion-dot {
+  width: 8px;
+  height: 8px;
+  background: var(--color-accent);
+  border-radius: 50%;
+}
+
+.demo-viewport[data-state="after"] .motion-list-item {
+  opacity: 0;
+  transform: translateY(12px);
+  animation: staggerIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
+}
+
+.demo-viewport[data-state="after"] .motion-list-item:nth-child(1) { animation-delay: 0s; }
+.demo-viewport[data-state="after"] .motion-list-item:nth-child(2) { animation-delay: 0.05s; }
+.demo-viewport[data-state="after"] .motion-list-item:nth-child(3) { animation-delay: 0.1s; }
+.demo-viewport[data-state="after"] .motion-list-item:nth-child(4) { animation-delay: 0.15s; }
+
+@keyframes staggerIn {
+  to { opacity: 1; transform: translateY(0); }
+}
+
+/* Micro-interactions */
+.motion-btn {
+  padding: 12px 24px;
+  font-size: 0.9375rem;
+  font-weight: 500;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+}
+
+.motion-btn-before {
+  background: var(--color-charcoal);
+  color: var(--color-paper);
+}
+
+.motion-btn-after {
+  background: var(--color-text);
+  color: var(--color-paper);
+  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s ease;
+}
+
+.motion-btn-after:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+}
+
+.motion-btn-after:active {
+  transform: translateY(0) scale(0.98);
+}
+
+/* State Transitions */
+.motion-card {
+  padding: var(--spacing-md);
+  background: var(--color-bg);
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+  text-align: center;
+  min-width: 140px;
+}
+
+.motion-card-icon { font-size: 1.5rem; margin-bottom: var(--spacing-xs); }
+.motion-card-text { font-size: 0.8125rem; color: var(--color-charcoal); }
+
+.motion-card-after {
+  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+.demo-viewport[data-state="after"] .motion-card-after {
+  background: color-mix(in oklch, var(--color-accent) 10%, var(--color-paper));
+  border-color: var(--color-accent);
+}
+
+.demo-viewport[data-state="after"] .motion-card-after .motion-card-icon {
+  animation: checkPop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+@keyframes checkPop {
+  50% { transform: scale(1.3); }
+}
+
+/* ============================================
+   TYPOGRAPHY DEMOS
+   ============================================ */
+
+.typo-demo {
+  width: 100%;
+  max-width: 320px;
+  text-align: left;
+}
+
+/* Font Pairing */
+.typo-pairing-before {
+  font-family: 'Inter', system-ui, sans-serif;
+}
+
+.typo-pairing-before .typo-heading {
+  font-size: 1.5rem;
+  font-weight: 600;
+  margin-bottom: var(--spacing-xs);
+}
+
+.typo-pairing-before .typo-body {
+  font-size: 0.9375rem;
+  line-height: 1.5;
+  color: var(--color-ash);
+}
+
+.typo-pairing-after .typo-heading {
+  font-family: var(--font-display);
+  font-size: 2rem;
+  font-weight: 300;
+  font-style: italic;
+  letter-spacing: -0.02em;
+  margin-bottom: var(--spacing-sm);
+  color: var(--color-text);
+}
+
+.typo-pairing-after .typo-body {
+  font-family: var(--font-body);
+  font-size: 0.9375rem;
+  line-height: 1.7;
+  color: var(--color-charcoal);
+}
+
+/* Hierarchy */
+.typo-hierarchy-before .typo-h1 { font-size: 1.125rem; font-weight: 600; margin-bottom: 4px; }
+.typo-hierarchy-before .typo-meta { font-size: 0.9375rem; color: var(--color-ash); margin-bottom: var(--spacing-xs); }
+.typo-hierarchy-before .typo-p { font-size: 0.875rem; line-height: 1.5; color: var(--color-charcoal); }
+
+.typo-hierarchy-after .typo-h1 {
+  font-family: var(--font-display);
+  font-size: 2.25rem;
+  font-weight: 300;
+  letter-spacing: -0.03em;
+  margin-bottom: 2px;
+  line-height: 1.1;
+}
+
+.typo-hierarchy-after .typo-meta {
+  font-size: 0.6875rem;
+  text-transform: uppercase;
+  letter-spacing: 0.12em;
+  color: var(--color-accent);
+  margin-bottom: var(--spacing-md);
+}
+
+.typo-hierarchy-after .typo-p {
+  font-size: 0.9375rem;
+  line-height: 1.7;
+  color: var(--color-ash);
+}
+
+/* ============================================
+   INTERACTION DESIGN DEMOS
+   ============================================ */
+
+.int-demo {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-md);
+  width: 100%;
+  max-width: 280px;
+}
+
+/* Button States */
+.int-states-demo {
+  gap: var(--spacing-lg);
+}
+
+.int-state-row {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+}
+
+.int-state-label {
+  font-size: 0.6875rem;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--color-ash);
+  width: 40px;
+}
+
+.int-btn {
+  flex: 1;
+  padding: var(--spacing-sm) var(--spacing-md);
+  font-size: 0.875rem;
+  font-weight: 500;
+  border-radius: 4px;
+  cursor: pointer;
+}
+
+.int-btn-poor {
+  background: var(--color-charcoal);
+  color: var(--color-paper);
+  border: none;
+}
+
+.int-btn-good {
+  background: var(--color-text);
+  color: var(--color-paper);
+  border: 2px solid transparent;
+  transition: all 0.15s ease;
+}
+
+.int-btn-good:hover {
+  background: var(--color-charcoal);
+}
+
+.int-btn-good:focus {
+  outline: none;
+  border-color: var(--color-accent);
+  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-accent) 25%, transparent);
+}
+
+.int-btn-good:active {
+  transform: scale(0.98);
+}
+
+/* Affordances */
+.int-aff-item {
+  padding: var(--spacing-sm) var(--spacing-md);
+  border-radius: 4px;
+  font-size: 0.875rem;
+  cursor: pointer;
+}
+
+.int-aff-poor {
+  color: var(--color-charcoal);
+}
+
+.int-aff-good {
+  color: var(--color-accent);
+  text-decoration: underline;
+  text-underline-offset: 2px;
+}
+
+.int-aff-good::after {
+  content: ' →';
+}
+
+.int-affordance-after .int-aff-item {
+  background: var(--color-bg);
+  border: 1px solid var(--color-mist);
+  color: var(--color-accent);
+  text-decoration: underline;
+  text-underline-offset: 2px;
+  transition: background 0.15s ease;
+}
+
+.int-affordance-after .int-aff-item:hover {
+  background: color-mix(in oklch, var(--color-accent) 5%, var(--color-paper));
+}
+
+.int-affordance-after .int-aff-item::after {
+  content: ' →';
+}
+
+/* Feedback */
+.int-feedback-before,
+.int-feedback-after {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+  flex-direction: row;
+}
+
+.int-fb-btn {
+  width: 48px;
+  height: 48px;
+  border-radius: 50%;
+  border: none;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.int-fb-btn svg {
+  width: 22px;
+  height: 22px;
+}
+
+.int-fb-silent {
+  background: var(--color-mist);
+  color: var(--color-ash);
+}
+
+.int-fb-active {
+  background: var(--color-charcoal);
+  color: var(--color-paper);
+  transition: all 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+.int-fb-active:hover {
+  transform: scale(1.1);
+}
+
+.int-fb-active:active {
+  transform: scale(0.95);
+}
+
+.int-fb-active.liked {
+  background: var(--color-accent);
+  animation: heartPop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+@keyframes heartPop {
+  50% { transform: scale(1.25); }
+}
+
+.int-fb-label {
+  font-size: 0.875rem;
+  color: var(--color-charcoal);
+}
+
+/* ============================================
+   COLOR & CONTRAST DEMOS
+   ============================================ */
+
+.color-demo {
+  width: 100%;
+  max-width: 300px;
+}
+
+/* Color Palette */
+.color-palette-before,
+.color-palette-after {
+  display: flex;
+  flex-wrap: wrap;
+  gap: var(--spacing-xs);
+  padding: var(--spacing-md);
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+}
+
+.color-swatch {
+  width: 40px;
+  height: 40px;
+  border-radius: 4px;
+  transition: background 0.2s ease;
+}
+
+.color-card {
+  width: 100%;
+  margin-top: var(--spacing-sm);
+  padding: var(--spacing-sm);
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 4px;
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.color-card span { font-size: 0.8125rem; font-weight: 500; transition: color 0.2s ease; }
+.color-card button { padding: 6px; border: none; border-radius: 3px; font-size: 0.75rem; font-weight: 500; cursor: pointer; transition: all 0.2s ease; }
+
+/* Before - Clashing colors */
+.color-palette-before .swatch-1 { background: #ff6b6b; }
+.color-palette-before .swatch-2 { background: #4ecdc4; }
+.color-palette-before .swatch-3 { background: #ffe66d; }
+.color-palette-before .swatch-4 { background: #95e1d3; }
+.color-palette-before .swatch-5 { background: #f38181; }
+.color-palette-before .card-title { color: #ff6b6b; }
+.color-palette-before .card-subtitle { color: #4ecdc4; }
+.color-palette-before .card-btn { background: #ffe66d; color: #333; }
+
+/* After - Harmonious palette */
+.color-palette-after .swatch-1 { background: var(--color-text); }
+.color-palette-after .swatch-2 { background: var(--color-charcoal); }
+.color-palette-after .swatch-3 { background: var(--color-ash); }
+.color-palette-after .swatch-4 { background: var(--color-mist); }
+.color-palette-after .swatch-5 { background: var(--color-accent); }
+.color-palette-after .card-title { color: var(--color-text); }
+.color-palette-after .card-subtitle { color: var(--color-ash); }
+.color-palette-after .card-btn { background: var(--color-accent); color: var(--color-paper); }
+
+/* Strategic Accent */
+.color-accent-card {
+  padding: var(--spacing-md);
+  border-radius: 6px;
+}
+
+.color-accent-before .color-accent-card {
+  background: #f5f5f5;
+  border: 1px solid #e0e0e0;
+}
+
+.color-accent-before .color-accent-title { font-weight: 600; color: #333; margin-bottom: 4px; }
+.color-accent-before .color-accent-text { font-size: 0.8125rem; color: #666; margin-bottom: var(--spacing-sm); }
+.color-accent-before .color-accent-btn { width: 100%; padding: var(--spacing-xs); background: #333; color: white; border: none; border-radius: 4px; font-size: 0.8125rem; cursor: pointer; }
+
+.color-accent-after .color-accent-card {
+  background: color-mix(in oklch, var(--color-accent) 8%, var(--color-paper));
+  border: 1px solid color-mix(in oklch, var(--color-accent) 20%, var(--color-paper));
+}
+
+.color-accent-after .color-accent-title { font-weight: 600; color: var(--color-text); margin-bottom: 4px; }
+.color-accent-after .color-accent-text { font-size: 0.8125rem; color: var(--color-ash); margin-bottom: var(--spacing-sm); }
+.color-accent-after .color-accent-btn { width: 100%; padding: var(--spacing-xs); background: var(--color-accent); color: var(--color-paper); border: none; border-radius: 4px; font-size: 0.8125rem; font-weight: 500; cursor: pointer; }
+
+/* Contrast Ratios */
+.color-contrast-static {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-sm);
+}
+
+.contrast-example {
+  padding: var(--spacing-md);
+  border-radius: 6px;
+  text-align: center;
+}
+
+.contrast-fail {
+  background: #f0f0f0;
+  color: #a0a0a0;
+}
+
+.contrast-pass {
+  background: var(--color-charcoal);
+  color: var(--color-paper);
+}
+
+.contrast-badge {
+  display: inline-block;
+  font-size: 0.5625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  padding: 2px 6px;
+  border-radius: 2px;
+  margin-bottom: 4px;
+}
+
+.contrast-fail .contrast-badge { background: #ddd; }
+.contrast-pass .contrast-badge { background: var(--color-accent); color: var(--color-paper); }
+
+.contrast-text { font-size: 1rem; font-weight: 500; margin-bottom: 2px; }
+.contrast-ratio { font-size: 0.6875rem; opacity: 0.7; }
+
+/* ============================================
+   RESPONSIVE DESIGN DEMOS
+   ============================================ */
+
+.resp-demo {
+  width: 100%;
+  max-width: 340px;
+}
+
+/* Touch Targets */
+.resp-touch-demo {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-lg);
+}
+
+.resp-touch-row {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+}
+
+.resp-label {
+  font-size: 0.6875rem;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--color-ash);
+  width: 70px;
+}
+
+.resp-touch-targets {
+  display: flex;
+  gap: 4px;
+}
+
+.resp-touch-targets button {
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-weight: 500;
+}
+
+.resp-touch-bad button {
+  width: 24px;
+  height: 24px;
+  font-size: 0.75rem;
+  background: var(--color-mist);
+  color: var(--color-ash);
+}
+
+.resp-touch-good button {
+  width: 44px;
+  height: 44px;
+  font-size: 1rem;
+  background: var(--color-text);
+  color: var(--color-paper);
+}
+
+/* Fluid Layout */
+.resp-fluid-demo {
+  padding: var(--spacing-md);
+  background: var(--color-bg);
+  border: 1px solid var(--color-mist);
+  border-radius: 6px;
+}
+
+.resp-fluid-container {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-md);
+}
+
+.resp-fluid-fixed,
+.resp-fluid-adaptive {
+  font-size: 0.75rem;
+  color: var(--color-ash);
+}
+
+.resp-fluid-fixed span,
+.resp-fluid-adaptive span {
+  display: block;
+  margin-bottom: 4px;
+}
+
+.resp-fluid-bar {
+  height: 24px;
+  background: var(--color-mist);
+  border-radius: 4px;
+}
+
+.resp-fluid-adaptive .resp-fluid-bar {
+  background: var(--color-accent);
+}
+
+/* Adaptive Content */
+.resp-adapt-demo {
+  display: flex;
+  gap: var(--spacing-sm);
+  align-items: flex-end;
+}
+
+.resp-device {
+  text-align: center;
+}
+
+.resp-device > span {
+  display: block;
+  margin-top: 4px;
+  font-size: 0.625rem;
+  color: var(--color-ash);
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+}
+
+.resp-device-screen {
+  background: var(--color-paper);
+  border: 2px solid var(--color-mist);
+  border-radius: 4px;
+  padding: 4px;
+  display: flex;
+  flex-direction: column;
+  gap: 3px;
+}
+
+.resp-device-mobile .resp-device-screen { width: 50px; height: 80px; }
+.resp-device-tablet .resp-device-screen { width: 80px; height: 60px; }
+.resp-device-desktop .resp-device-screen { width: 120px; height: 70px; }
+
+.resp-block {
+  background: var(--color-mist);
+  border-radius: 2px;
+}
+
+.resp-block-row {
+  display: flex;
+  gap: 3px;
+  flex: 1;
+}
+
+.resp-header { height: 16px; background: var(--color-charcoal); }
+.resp-sidebar { width: 30%; background: var(--color-charcoal); }
+.resp-content { flex: 1; }
+
+/* ============================================
+   ANIMATIONS
+   ============================================ */
+
+@keyframes fadeIn {
+  from { opacity: 0; }
+  to { opacity: 1; }
+}
+
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}

public/css/sub-pages.css 🔗

@@ -0,0 +1,2070 @@
+/*
+ * impeccable.style — shared styles for sub-pages and the site-wide header
+ *
+ * Imported by all generated pages under /skills, /anti-patterns, /tutorials,
+ * and by the hand-authored pages (index, cheatsheet, gallery, privacy) via
+ * a direct <link> tag so they all share the same header + prose typography.
+ */
+
+@import "./tokens.css";
+
+/* ============================================
+   SKIP LINK (a11y)
+   ============================================ */
+
+.skip-link {
+  position: absolute;
+  top: -100%;
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 10000;
+  padding: var(--spacing-sm) var(--spacing-lg);
+  background: var(--color-ink);
+  color: var(--color-paper);
+  font-weight: 600;
+  text-decoration: none;
+  border-radius: 0 0 8px 8px;
+  transition: top 0.2s ease;
+}
+
+.skip-link:focus {
+  top: 0;
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+
+/* ============================================
+   BASE
+   ============================================ */
+
+html {
+  scroll-behavior: smooth;
+}
+
+body {
+  font-family: var(--font-body);
+  color: var(--color-ink);
+  background: var(--color-paper);
+  line-height: 1.6;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-rendering: optimizeLegibility;
+}
+
+a {
+  color: inherit;
+  text-decoration: none;
+}
+
+/* ============================================
+   SHARED SITE HEADER
+   ============================================ */
+
+.site-header {
+  position: sticky;
+  top: 0;
+  z-index: 100;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: var(--spacing-md);
+  padding: 14px clamp(var(--spacing-md), 4vw, var(--spacing-2xl));
+  background: color-mix(in oklch, var(--color-paper) 94%, transparent);
+  -webkit-backdrop-filter: saturate(1.4) blur(16px);
+  backdrop-filter: saturate(1.4) blur(16px);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+/* Kill any inherited text-decoration from page-level `a` rules. */
+.site-header a,
+.site-header a:hover {
+  text-decoration: none;
+  text-decoration-thickness: 0;
+}
+
+.site-header-brand {
+  display: inline-flex;
+  align-items: center;
+  gap: 10px;
+  font-family: var(--font-display);
+  font-size: 1.25rem;
+  font-weight: 600;
+  letter-spacing: -0.01em;
+  color: var(--color-ink);
+  flex-shrink: 0;
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+.site-header-brand:hover {
+  color: var(--color-accent);
+}
+
+.site-header-brand-logo {
+  width: 26px;
+  height: 26px;
+  border-radius: 6px;
+  flex-shrink: 0;
+}
+
+.site-header-brand-name {
+  display: inline-block;
+}
+
+.site-header-right {
+  display: flex;
+  align-items: center;
+  gap: clamp(var(--spacing-sm), 2vw, var(--spacing-md));
+  min-width: 0;
+}
+
+.site-header-nav {
+  display: flex;
+  align-items: center;
+  gap: clamp(var(--spacing-sm), 2vw, var(--spacing-md));
+  font-family: var(--font-body);
+  font-size: 0.9375rem;
+  font-weight: 500;
+  min-width: 0;
+}
+
+.site-header-nav a {
+  position: relative;
+  display: inline-block;
+  white-space: nowrap;
+  color: var(--color-charcoal);
+  padding: 4px 0;
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+.site-header-nav a:hover {
+  color: var(--color-accent);
+}
+
+.site-header-nav a[aria-current="page"] {
+  color: var(--color-ink);
+  font-weight: 600;
+}
+
+.site-header-nav a[aria-current="page"]::after {
+  content: "";
+  position: absolute;
+  left: 0;
+  right: 0;
+  bottom: -4px;
+  height: 2px;
+  background: var(--color-accent);
+  border-radius: 2px;
+}
+
+/* GitHub star pill — subtle, inviting nudge without shouting */
+.site-header-github {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  padding: 4px 10px 4px 8px;
+  border-radius: 99px;
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  font-family: var(--font-mono);
+  font-size: 0.8125rem;
+  font-weight: 500;
+  color: var(--color-charcoal);
+  flex-shrink: 0;
+  transition:
+    background var(--duration-fast) var(--ease-out),
+    border-color var(--duration-fast) var(--ease-out),
+    color var(--duration-fast) var(--ease-out);
+}
+
+.site-header-github svg {
+  width: 14px;
+  height: 14px;
+  flex-shrink: 0;
+}
+
+.site-header-github:hover {
+  background: var(--color-paper);
+  border-color: var(--color-ink);
+  color: var(--color-ink);
+}
+
+.site-header-github-star {
+  color: #f59e0b;
+  width: 12px;
+  height: 12px;
+  flex-shrink: 0;
+}
+
+@media (max-width: 820px) {
+  .site-header {
+    padding: 12px var(--spacing-md);
+    gap: var(--spacing-sm);
+  }
+
+  .site-header-brand {
+    font-size: 1.0625rem;
+  }
+
+  .site-header-brand-logo {
+    width: 22px;
+    height: 22px;
+  }
+
+  .site-header-right {
+    gap: var(--spacing-sm);
+  }
+
+  .site-header-nav {
+    font-size: 0.8125rem;
+    gap: var(--spacing-sm);
+    overflow-x: auto;
+    scrollbar-width: none;
+    -webkit-overflow-scrolling: touch;
+    /* fade the right edge so users see there's more to scroll */
+    mask-image: linear-gradient(to right, black 0%, black calc(100% - 24px), transparent 100%);
+    -webkit-mask-image: linear-gradient(to right, black 0%, black calc(100% - 24px), transparent 100%);
+  }
+
+  .site-header-nav::-webkit-scrollbar {
+    display: none;
+  }
+
+  .site-header-nav a[data-nav="home"] {
+    /* Brand already acts as home link on mobile */
+    display: none;
+  }
+
+  .site-header-github {
+    padding: 4px 8px;
+  }
+
+  .site-header-github-label {
+    display: none;
+  }
+}
+
+@media (max-width: 480px) {
+  .site-header-brand-name {
+    /* Logo alone on narrow screens */
+    display: none;
+  }
+}
+
+/* ============================================
+   SUB-PAGE LAYOUT SHELL
+   ============================================ */
+
+main#main {
+  display: block;
+}
+
+.sub-page-content {
+  max-width: 820px;
+  margin: 0 auto;
+  padding: clamp(2rem, 5vw, 4rem) clamp(1.25rem, 4vw, 2.5rem) 6rem;
+}
+
+.skill-detail {
+  /* No max-width on the article itself. The editorial body sections
+     (header, editorial wrapper, skill source, references) each cap
+     themselves at 720px for readable line length, while the hero is
+     allowed to break out on wide viewports. */
+  width: 100%;
+}
+
+.sub-page-header {
+  margin-bottom: clamp(2.5rem, 6vw, 4rem);
+}
+
+.sub-page-eyebrow {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.14em;
+  color: var(--color-accent);
+  margin-bottom: var(--spacing-sm);
+}
+
+.sub-page-title {
+  font-family: var(--font-display);
+  font-size: clamp(2.5rem, 6vw, 4.5rem);
+  font-weight: 400;
+  line-height: 1.05;
+  letter-spacing: -0.02em;
+  color: var(--color-ink);
+  margin-bottom: var(--spacing-md);
+}
+
+.sub-page-lede {
+  font-size: clamp(1rem, 1.4vw, 1.125rem);
+  line-height: 1.55;
+  color: var(--color-charcoal);
+  max-width: 62ch;
+}
+
+/* ============================================
+   DOCS-BROWSER LAYOUT (used by /skills)
+   ============================================ */
+
+:root {
+  --site-header-height: 62px;
+  --skills-sidebar-width: 200px;
+}
+
+.skills-layout-page main#main {
+  /* Reset: the docs layout provides its own max-width container. */
+  max-width: none;
+  margin: 0;
+  padding: 0;
+}
+
+.skills-layout {
+  display: grid;
+  grid-template-columns: var(--skills-sidebar-width) minmax(0, 1fr);
+  gap: clamp(var(--spacing-lg), 4vw, var(--spacing-2xl));
+  /*
+   * Match horizontal padding to .site-header so the sidebar content lines
+   * up with the brand logo in the nav bar above. Intentionally no max-width
+   * so the docs-browser feels wall-to-wall on wide screens.
+   */
+  padding: 0 clamp(var(--spacing-md), 4vw, var(--spacing-2xl));
+  align-items: start;
+}
+
+/* ============================================
+   SIDEBAR
+   ============================================ */
+
+.skills-sidebar {
+  position: sticky;
+  top: var(--site-header-height);
+  align-self: start;
+  max-height: calc(100vh - var(--site-header-height));
+  overflow-y: auto;
+  padding: var(--spacing-lg) 0 var(--spacing-2xl);
+  border-right: 1px solid var(--color-mist);
+  scrollbar-width: thin;
+  scrollbar-color: var(--color-mist) transparent;
+}
+
+/* Desktop only: reserve full viewport height so the border-right
+   divider reaches the bottom of the viewport even on short pages.
+   On mobile this rule would leave a viewport-sized empty block
+   above the main content when the menu is collapsed. */
+@media (min-width: 921px) {
+  .skills-sidebar {
+    min-height: calc(100vh - var(--site-header-height));
+  }
+}
+
+.skills-sidebar::-webkit-scrollbar {
+  width: 6px;
+}
+
+.skills-sidebar::-webkit-scrollbar-thumb {
+  background: var(--color-mist);
+  border-radius: 3px;
+}
+
+.skills-sidebar-inner {
+  padding-right: var(--spacing-md);
+}
+
+/* Mobile menu toggle. Hidden on desktop. */
+.skills-sidebar-toggle {
+  display: none;
+}
+
+.skills-sidebar-toggle-chevron {
+  transition: transform var(--duration-base) var(--ease-out);
+  flex-shrink: 0;
+  color: var(--color-ash);
+}
+
+/* Label at the top of the sidebar — hidden visually, kept for a11y tree. */
+.skills-sidebar-label {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  border: 0;
+}
+
+.skills-sidebar-group {
+  margin-bottom: 1.5rem;
+}
+
+.skills-sidebar-group:last-child {
+  margin-bottom: 0;
+}
+
+.skills-sidebar-divider {
+  border: none;
+  height: 1px;
+  background: var(--color-mist);
+  margin: 0 0 1.5rem 0;
+  width: 100%;
+}
+
+.skills-sidebar-group-title {
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.14em;
+  color: var(--color-ash);
+  margin-bottom: 6px;
+  /* Match the link text's 14px left inset (2 border + 12 padding) so
+     category titles and link labels start at the same x position. */
+  padding: 0 0 0 14px;
+}
+
+.skills-sidebar-list {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.skills-sidebar-list li {
+  margin: 0;
+}
+
+.skills-sidebar-list a {
+  display: block;
+  /* Border sits inside the normal flow (no negative margin) because
+     the sidebar's `overflow-y: auto` coerces overflow-x to auto too
+     per CSS spec, which would clip any border hanging left of the
+     column. Text is inset 14px (2 border + 12 padding) from the column
+     edge; group titles above use the same 14px inset. Accepts a small
+     misalignment with the header logo in exchange for a visible
+     active-state accent. */
+  padding: 4px 0 4px 12px;
+  border-left: 2px solid transparent;
+  font-family: var(--font-mono);
+  font-size: 0.875rem;
+  font-weight: 500;
+  line-height: 1.5;
+  color: var(--color-charcoal);
+  text-decoration: none;
+  transition: color var(--duration-fast) var(--ease-out),
+              border-color var(--duration-fast) var(--ease-out),
+              background var(--duration-fast) var(--ease-out);
+}
+
+.skills-sidebar-list a:hover {
+  color: var(--color-accent);
+  background: color-mix(in oklch, var(--color-accent-dim) 30%, transparent);
+}
+
+.skills-sidebar-list a[aria-current="page"] {
+  color: var(--color-ink);
+  font-weight: 600;
+  border-left-color: var(--color-accent);
+}
+
+.skills-sidebar-list a[aria-current="page"]:hover {
+  color: var(--color-ink);
+  background: transparent;
+}
+
+.skills-sidebar-sub a {
+  padding-left: 24px;
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+}
+
+/* ============================================
+   OVERVIEW MAIN CONTENT
+   ============================================ */
+
+.skills-main {
+  min-width: 0;
+  padding: clamp(2rem, 4vw, 3.5rem) 0 clamp(4rem, 8vw, 6rem);
+}
+
+.skills-overview-content {
+  max-width: 720px;
+}
+
+.skills-overview-header {
+  margin-bottom: clamp(2.5rem, 5vw, 4rem);
+}
+
+.skills-overview-header .sub-page-lede a {
+  color: var(--color-ink);
+  text-decoration: underline;
+  text-decoration-thickness: 1px;
+  text-decoration-color: var(--color-accent);
+  text-underline-offset: 4px;
+  font-family: var(--font-mono);
+  font-weight: 500;
+}
+
+.skills-overview-howto {
+  padding: var(--spacing-lg) var(--spacing-lg);
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  border-radius: 10px;
+  margin-bottom: clamp(2.5rem, 5vw, 4rem);
+}
+
+.skills-overview-howto-title {
+  font-family: var(--font-display);
+  font-size: 1.25rem;
+  font-style: italic;
+  font-weight: 500;
+  color: var(--color-ink);
+  margin-bottom: var(--spacing-sm);
+}
+
+.skills-overview-howto p {
+  font-size: 0.9375rem;
+  line-height: 1.7;
+  color: var(--color-charcoal);
+  max-width: 60ch;
+}
+
+.skills-overview-howto a {
+  color: var(--color-ink);
+  font-family: var(--font-mono);
+  font-size: 0.875em;
+  font-weight: 500;
+  text-decoration: none;
+  border-bottom: 1px solid var(--color-accent);
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+.skills-overview-howto a:hover {
+  color: var(--color-accent);
+}
+
+.skills-overview-categories {
+  display: flex;
+  flex-direction: column;
+  gap: clamp(2rem, 4vw, 3rem);
+}
+
+.skills-overview-category {
+  padding-bottom: clamp(2rem, 4vw, 2.5rem);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.skills-overview-category:last-child {
+  border-bottom: none;
+}
+
+.skills-overview-category-meta {
+  display: flex;
+  align-items: baseline;
+  justify-content: space-between;
+  gap: var(--spacing-md);
+  margin-bottom: 6px;
+}
+
+.skills-overview-category-title {
+  font-family: var(--font-display);
+  font-size: clamp(1.5rem, 3vw, 2rem);
+  font-weight: 500;
+  font-style: italic;
+  color: var(--color-ink);
+  letter-spacing: -0.01em;
+}
+
+.skills-overview-category-count {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.12em;
+  color: var(--color-ash);
+}
+
+.skills-overview-category-desc {
+  font-size: 0.9375rem;
+  line-height: 1.7;
+  color: var(--color-charcoal);
+  max-width: 58ch;
+  margin-bottom: var(--spacing-md);
+}
+
+.skills-overview-chips {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+
+.skills-overview-chip {
+  display: inline-flex;
+  align-items: center;
+  padding: 6px 12px;
+  font-family: var(--font-mono);
+  font-size: 0.8125rem;
+  font-weight: 500;
+  color: var(--color-ink);
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 99px;
+  text-decoration: none;
+  transition: border-color var(--duration-fast) var(--ease-out),
+              color var(--duration-fast) var(--ease-out),
+              background var(--duration-fast) var(--ease-out);
+}
+
+.skills-overview-chip:hover {
+  color: var(--color-accent);
+  border-color: var(--color-accent);
+  background: var(--color-cream);
+}
+
+/* ============================================
+   ANTI-PATTERNS INDEX
+   ============================================ */
+
+.anti-patterns-sidebar-list a {
+  display: flex !important;
+  align-items: baseline;
+  justify-content: space-between;
+  gap: var(--spacing-sm);
+}
+
+.anti-patterns-sidebar-count {
+  font-size: 0.6875rem;
+  font-weight: 500;
+  color: var(--color-ash);
+  font-variant-numeric: tabular-nums;
+}
+
+/* The anti-patterns page lets the grid of rule cards use the full
+   column width. Header, lede, and legend stay capped for line length. */
+.anti-patterns-content {
+  width: 100%;
+}
+
+.anti-patterns-header {
+  max-width: 720px;
+  margin-bottom: clamp(2rem, 4vw, 3rem);
+}
+
+.anti-patterns-header .sub-page-lede code {
+  font-family: var(--font-mono);
+  font-size: 0.875em;
+  color: var(--color-ink);
+  background: var(--color-cream);
+  padding: 2px 6px;
+  border-radius: 4px;
+  border: 1px solid var(--color-mist);
+}
+
+.anti-patterns-header .sub-page-lede a {
+  color: var(--color-ink);
+  text-decoration: underline;
+  text-decoration-color: var(--color-accent);
+  text-decoration-thickness: 1px;
+  text-underline-offset: 4px;
+  font-family: var(--font-mono);
+  font-weight: 500;
+  font-size: 0.9375em;
+}
+
+.anti-patterns-legend {
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  border-radius: 10px;
+  margin-bottom: clamp(2rem, 4vw, 3rem);
+  max-width: 720px;
+}
+
+.anti-patterns-legend-summary {
+  list-style: none;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: var(--spacing-sm);
+  padding: 14px var(--spacing-lg);
+  transition: background var(--duration-fast) var(--ease-out);
+}
+
+.anti-patterns-legend-summary::-webkit-details-marker {
+  display: none;
+}
+
+.anti-patterns-legend-summary:hover {
+  background: color-mix(in oklch, var(--color-mist) 40%, transparent);
+}
+
+.anti-patterns-legend-title {
+  font-family: var(--font-body);
+  font-size: 0.875rem;
+  font-weight: 600;
+  color: var(--color-ink);
+}
+
+.anti-patterns-legend-chevron {
+  color: var(--color-ash);
+  transition: transform var(--duration-base) var(--ease-out);
+  flex-shrink: 0;
+}
+
+.anti-patterns-legend[open] .anti-patterns-legend-chevron {
+  transform: rotate(180deg);
+}
+
+.anti-patterns-legend-body {
+  padding: 0 var(--spacing-lg) var(--spacing-lg);
+  border-top: 1px solid var(--color-mist);
+  margin-top: 4px;
+  padding-top: var(--spacing-md);
+}
+
+.anti-patterns-legend-body p {
+  font-size: 0.9375rem;
+  line-height: 1.7;
+  color: var(--color-charcoal);
+}
+
+.anti-patterns-legend-body a {
+  color: var(--color-ink);
+  text-decoration: none;
+  border-bottom: 1px solid var(--color-accent);
+  font-family: var(--font-mono);
+  font-size: 0.875em;
+  font-weight: 500;
+}
+
+.anti-patterns-legend-body a:hover {
+  color: var(--color-accent);
+}
+
+.anti-patterns-sections {
+  display: flex;
+  flex-direction: column;
+  gap: clamp(3rem, 6vw, 4.5rem);
+}
+
+.anti-patterns-section-header {
+  display: flex;
+  align-items: baseline;
+  justify-content: space-between;
+  gap: var(--spacing-md);
+  padding-bottom: var(--spacing-sm);
+  border-bottom: 1px solid var(--color-mist);
+  margin-bottom: var(--spacing-lg);
+}
+
+.anti-patterns-section-title {
+  font-family: var(--font-display);
+  font-size: clamp(1.75rem, 3vw, 2.25rem);
+  font-weight: 500;
+  font-style: italic;
+  color: var(--color-ink);
+  letter-spacing: -0.01em;
+}
+
+.anti-patterns-section-count {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.12em;
+  color: var(--color-ash);
+}
+
+.rule-card-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
+  gap: var(--spacing-md);
+}
+
+.rule-card {
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 10px;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  transition: border-color var(--duration-fast) var(--ease-out);
+}
+
+.rule-card:hover {
+  border-color: var(--color-ash);
+}
+
+/* Visual example preview at the top of each card. */
+.rule-card-visual {
+  position: relative;
+  height: 160px;
+  background: var(--color-cream);
+  border-bottom: 1px solid var(--color-mist);
+  overflow: hidden;
+}
+
+.rule-card-visual-inner {
+  position: absolute;
+  inset: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: var(--spacing-md);
+  /* Prevent the inline snippet's styles from bleeding outside the box. */
+  overflow: hidden;
+}
+
+.rule-card-body {
+  padding: var(--spacing-md);
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  flex: 1;
+}
+
+.rule-card-head {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  margin-bottom: 2px;
+  flex-wrap: wrap;
+}
+
+.rule-card-category {
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  padding: 3px 8px;
+  border-radius: 99px;
+}
+
+.rule-card-category[data-category="slop"] {
+  color: var(--color-accent);
+  background: var(--color-accent-dim);
+}
+
+.rule-card-category[data-category="quality"] {
+  color: var(--color-charcoal);
+  background: var(--color-mist);
+}
+
+/* Detection layer badge: CLI, Browser, or LLM only. */
+.rule-card-layer {
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  padding: 3px 8px;
+  border-radius: 99px;
+  border: 1px solid var(--color-mist);
+}
+
+.rule-card-layer[data-layer="cli"] {
+  color: var(--color-charcoal);
+  border-color: var(--color-mist);
+  background: var(--color-paper);
+}
+
+.rule-card-layer[data-layer="browser"] {
+  color: oklch(40% 0.12 230);
+  border-color: oklch(90% 0.05 230);
+  background: oklch(97% 0.02 230);
+}
+
+.rule-card-layer[data-layer="llm"] {
+  color: oklch(45% 0.15 45);
+  border-color: oklch(92% 0.08 45);
+  background: oklch(98% 0.03 45);
+}
+
+/* Layer legend dl inside the How-to-read block. */
+.anti-patterns-legend-layers {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-sm);
+  margin-top: var(--spacing-md);
+}
+
+.anti-patterns-legend-layers > div {
+  display: grid;
+  grid-template-columns: 90px 1fr;
+  gap: var(--spacing-md);
+  align-items: baseline;
+}
+
+.anti-patterns-legend-layers dt {
+  margin: 0;
+}
+
+.anti-patterns-legend-layers dd {
+  margin: 0;
+  font-size: 0.875rem;
+  line-height: 1.55;
+  color: var(--color-charcoal);
+}
+
+.anti-patterns-legend-layers dd code {
+  font-family: var(--font-mono);
+  font-size: 0.8125rem;
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  padding: 1px 6px;
+  border-radius: 4px;
+}
+
+.rule-card-name {
+  font-family: var(--font-body);
+  font-size: 1rem;
+  font-weight: 600;
+  color: var(--color-ink);
+  line-height: 1.35;
+}
+
+.rule-card-desc {
+  font-size: 0.875rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  flex: 1;
+}
+
+.rule-card-skill-link {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  font-weight: 500;
+  color: var(--color-charcoal);
+  text-decoration: none;
+  align-self: flex-start;
+  padding-top: 4px;
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+.rule-card-skill-link:hover {
+  color: var(--color-accent);
+}
+
+.rule-card-skill-link::after {
+  content: " →";
+}
+
+/* ============================================
+   TUTORIALS INDEX
+   ============================================ */
+
+.tutorials-content {
+  max-width: 720px;
+}
+
+.tutorial-cards {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-sm);
+}
+
+.tutorial-card {
+  display: grid;
+  grid-template-columns: auto 1fr auto;
+  gap: var(--spacing-md);
+  align-items: center;
+  padding: var(--spacing-md) var(--spacing-lg);
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 10px;
+  color: var(--color-ink);
+  text-decoration: none;
+  transition: border-color var(--duration-fast) var(--ease-out),
+              transform var(--duration-fast) var(--ease-out);
+}
+
+.tutorial-card:hover {
+  border-color: var(--color-ink);
+}
+
+.tutorial-card-number {
+  font-family: var(--font-mono);
+  font-size: 1.5rem;
+  font-weight: 500;
+  color: var(--color-ash);
+  line-height: 1;
+  font-variant-numeric: tabular-nums;
+}
+
+.tutorial-card:hover .tutorial-card-number {
+  color: var(--color-accent);
+}
+
+.tutorial-card-body {
+  min-width: 0;
+}
+
+.tutorial-card-title {
+  font-family: var(--font-display);
+  font-size: 1.375rem;
+  font-style: italic;
+  font-weight: 500;
+  color: var(--color-ink);
+  line-height: 1.2;
+  margin-bottom: 4px;
+}
+
+.tutorial-card-tagline {
+  font-size: 0.9375rem;
+  color: var(--color-charcoal);
+  line-height: 1.5;
+}
+
+.tutorial-card-arrow {
+  font-family: var(--font-body);
+  font-size: 1.25rem;
+  color: var(--color-ash);
+  transition: color var(--duration-fast) var(--ease-out),
+              transform var(--duration-fast) var(--ease-out);
+}
+
+.tutorial-card:hover .tutorial-card-arrow {
+  color: var(--color-accent);
+  transform: translateX(4px);
+}
+
+/* ============================================
+   TUTORIAL EMBEDS (live overlays, screenshots)
+   ============================================ */
+
+.tutorial-embed {
+  margin: var(--spacing-lg) 0 var(--spacing-xl);
+  border-radius: 10px;
+  overflow: hidden;
+  border: 1px solid var(--color-mist);
+  box-shadow: 0 8px 30px -6px rgba(0, 0, 0, 0.12);
+  max-width: none;
+}
+
+.tutorial-embed-header {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  padding: 10px 14px;
+  background: var(--color-cream);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.tutorial-embed-dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  flex-shrink: 0;
+}
+
+.tutorial-embed-dot.red { background: #ff5f56; }
+.tutorial-embed-dot.yellow { background: #ffbd2e; }
+.tutorial-embed-dot.green { background: #27c93f; }
+
+.tutorial-embed-title {
+  margin-left: auto;
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  color: var(--color-ash);
+}
+
+.tutorial-embed-iframe {
+  display: block;
+  width: 100%;
+  height: 520px;
+  border: none;
+  background: white;
+}
+
+.tutorial-embed-caption {
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+  margin-top: var(--spacing-sm);
+  font-style: italic;
+  max-width: 60ch;
+}
+
+/* ============================================
+   TUTORIAL DETAIL
+   ============================================ */
+
+.tutorial-detail {
+  max-width: 680px;
+}
+
+.tutorial-detail-header {
+  margin-bottom: clamp(2.5rem, 5vw, 3.5rem);
+}
+
+.tutorial-detail-title {
+  font-family: var(--font-display);
+  font-size: clamp(2.5rem, 6vw, 4.5rem);
+  font-weight: 400;
+  line-height: 1.05;
+  letter-spacing: -0.02em;
+  color: var(--color-ink);
+  margin-bottom: var(--spacing-md);
+}
+
+.tutorial-detail-tagline {
+  font-family: var(--font-body);
+  font-size: clamp(1.0625rem, 1.5vw, 1.1875rem);
+  line-height: 1.55;
+  color: var(--color-charcoal);
+  max-width: 60ch;
+}
+
+.tutorial-detail-body {
+  max-width: 65ch;
+}
+
+/* ============================================
+   VISUAL MODE PAGE
+   ============================================ */
+
+.visual-mode-page-body main#main {
+  /* Reset the docs-layout override; this page uses its own max-width. */
+  max-width: none;
+  margin: 0;
+  padding: 0;
+}
+
+.visual-mode-page {
+  max-width: 1100px;
+  margin: 0 auto;
+  padding: clamp(2rem, 4vw, 3.5rem) clamp(var(--spacing-md), 4vw, var(--spacing-2xl)) clamp(4rem, 8vw, 6rem);
+}
+
+.visual-mode-page-header {
+  max-width: 720px;
+  margin-bottom: clamp(2.5rem, 5vw, 3.5rem);
+}
+
+.visual-mode-demo-wrap {
+  margin-bottom: clamp(3rem, 6vw, 4.5rem);
+}
+
+.visual-mode-demo-caption {
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+  font-style: italic;
+  margin-top: var(--spacing-sm);
+  text-align: center;
+}
+
+/* Mac-window chrome around the iframe. Scoped to .visual-mode-page so
+   the sub-page's preview styles don't leak onto the homepage, which
+   carries its own .visual-mode-preview rules in main.css. The auto
+   margins below would disable grid-item stretch on the homepage and
+   collapse the preview to the iframe's 300px intrinsic width. */
+.visual-mode-page .visual-mode-preview {
+  border-radius: 10px;
+  overflow: hidden;
+  border: 1px solid var(--color-mist);
+  box-shadow: 0 12px 40px -8px rgba(0, 0, 0, 0.12);
+  max-width: 1040px;
+  margin: 0 auto;
+}
+
+.visual-mode-page .visual-mode-preview-header {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  padding: 10px 14px;
+  background: var(--color-cream);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.visual-mode-page .visual-mode-preview-dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  flex-shrink: 0;
+}
+
+.visual-mode-page .visual-mode-preview-dot.red { background: #ff5f56; }
+.visual-mode-page .visual-mode-preview-dot.yellow { background: #ffbd2e; }
+.visual-mode-page .visual-mode-preview-dot.green { background: #27c93f; }
+
+.visual-mode-page .visual-mode-preview-title {
+  margin-left: auto;
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  color: var(--color-ash);
+}
+
+.visual-mode-frame {
+  display: block;
+  width: 100%;
+  height: 580px;
+  border: none;
+  background: white;
+}
+
+/* Three invocation methods. */
+.visual-mode-methods {
+  margin-bottom: clamp(3rem, 6vw, 4.5rem);
+}
+
+.visual-mode-methods-title {
+  font-family: var(--font-display);
+  font-size: clamp(1.75rem, 3vw, 2.25rem);
+  font-weight: 500;
+  font-style: italic;
+  color: var(--color-ink);
+  letter-spacing: -0.01em;
+  margin-bottom: var(--spacing-lg);
+}
+
+.visual-mode-methods-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+  gap: var(--spacing-md);
+}
+
+.visual-mode-method {
+  padding: var(--spacing-lg);
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 10px;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  transition: border-color var(--duration-fast) var(--ease-out);
+}
+
+.visual-mode-method:hover {
+  border-color: var(--color-ash);
+}
+
+.visual-mode-method[data-coming-soon] {
+  background: var(--color-cream);
+}
+
+.visual-mode-method-label {
+  font-family: var(--font-mono);
+  font-size: 0.625rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.14em;
+  color: var(--color-accent);
+}
+
+.visual-mode-method-name {
+  font-family: var(--font-display);
+  font-size: 1.375rem;
+  font-style: italic;
+  font-weight: 500;
+  color: var(--color-ink);
+  line-height: 1.25;
+}
+
+.visual-mode-method-name a {
+  color: inherit;
+  text-decoration: none;
+  border-bottom: 1px solid var(--color-accent);
+}
+
+.visual-mode-method-name a:hover {
+  color: var(--color-accent);
+}
+
+.visual-mode-method-name code {
+  font-family: var(--font-mono);
+  font-size: 0.875em;
+  font-style: normal;
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  padding: 2px 8px;
+  border-radius: 4px;
+  color: var(--color-ink);
+}
+
+.visual-mode-method-desc {
+  font-size: 0.9375rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  flex: 1;
+}
+
+.visual-mode-method-desc a {
+  color: var(--color-ink);
+  text-decoration: none;
+  border-bottom: 1px solid var(--color-accent);
+  font-family: var(--font-mono);
+  font-size: 0.875em;
+  font-weight: 500;
+}
+
+.visual-mode-method-desc a:hover {
+  color: var(--color-accent);
+}
+
+/* Gallery (specimens) section */
+.visual-mode-gallery-header {
+  margin-bottom: var(--spacing-lg);
+}
+
+.visual-mode-gallery-title {
+  font-family: var(--font-display);
+  font-size: clamp(1.75rem, 3vw, 2.25rem);
+  font-weight: 500;
+  font-style: italic;
+  color: var(--color-ink);
+  letter-spacing: -0.01em;
+  margin-bottom: var(--spacing-sm);
+}
+
+.visual-mode-gallery-lede {
+  font-size: 0.9375rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  max-width: 60ch;
+}
+
+/* ============================================
+   ANTI-PATTERNS: IN THE WILD (merged gallery)
+   ============================================ */
+
+.gallery-section {
+  margin-top: clamp(3rem, 6vw, 4.5rem);
+}
+
+.gallery-section-lede {
+  font-size: 0.9375rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  max-width: 60ch;
+  margin-bottom: var(--spacing-lg);
+}
+
+.gallery-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
+  gap: var(--spacing-md);
+}
+
+.gallery-card {
+  display: flex;
+  flex-direction: column;
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 10px;
+  overflow: hidden;
+  text-decoration: none;
+  color: inherit;
+  transition: border-color var(--duration-fast) var(--ease-out),
+              transform var(--duration-fast) var(--ease-out);
+}
+
+.gallery-card:hover {
+  border-color: var(--color-ink);
+  transform: translateY(-2px);
+}
+
+.gallery-card-thumb {
+  aspect-ratio: 1;
+  overflow: hidden;
+  background: var(--color-cream);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.gallery-card-thumb img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  object-position: top left;
+  display: block;
+}
+
+.gallery-card-body {
+  padding: var(--spacing-md);
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  flex: 1;
+}
+
+.gallery-card-title {
+  font-family: var(--font-display);
+  font-size: 1.125rem;
+  font-style: italic;
+  font-weight: 500;
+  color: var(--color-ink);
+  line-height: 1.25;
+}
+
+.gallery-card:hover .gallery-card-title {
+  color: var(--color-accent);
+}
+
+.gallery-card-desc {
+  font-size: 0.8125rem;
+  line-height: 1.55;
+  color: var(--color-charcoal);
+}
+
+/* ============================================
+   MOBILE: collapsible sidebar behind a toggle
+   ============================================ */
+
+@media (max-width: 920px) {
+  .skills-layout {
+    grid-template-columns: 1fr;
+    gap: 0;
+  }
+
+  .skills-sidebar {
+    position: static;
+    max-height: none;
+    overflow: visible;
+    border-right: none;
+    padding: var(--spacing-md) 0;
+    margin-bottom: var(--spacing-lg);
+  }
+
+  /* Show the toggle button and collapse the menu by default. */
+  .skills-sidebar-toggle {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: var(--spacing-sm);
+    width: 100%;
+    padding: 12px 16px;
+    background: var(--color-cream);
+    border: 1px solid var(--color-mist);
+    border-radius: 8px;
+    cursor: pointer;
+    font-family: var(--font-mono);
+    font-size: 0.8125rem;
+    font-weight: 600;
+    color: var(--color-ink);
+    text-align: left;
+    transition: border-color var(--duration-fast) var(--ease-out);
+  }
+
+  .skills-sidebar-toggle:hover {
+    border-color: var(--color-ink);
+  }
+
+  .skills-sidebar-toggle[aria-expanded="true"] .skills-sidebar-toggle-chevron {
+    transform: rotate(180deg);
+  }
+
+  .skills-sidebar-inner {
+    display: none;
+    padding-right: 0;
+    padding-top: var(--spacing-md);
+    border-top: 1px solid var(--color-mist);
+    margin-top: var(--spacing-md);
+  }
+
+  .skills-sidebar-toggle[aria-expanded="true"] + .skills-sidebar-inner {
+    display: block;
+  }
+
+  .skills-sidebar-group {
+    margin-bottom: var(--spacing-md);
+  }
+}
+
+/* ============================================
+   SKILL DETAIL — BEFORE/AFTER DEMO
+   ============================================ */
+
+/* Ported from main.css for use on sub-pages. The split-compare effect
+   is initialized by js/effects/split-compare.js loaded on demand. */
+
+.split-comparison {
+  position: relative;
+  width: 100%;
+  /* 500px visible + 64px buffer (32px per side) = 564px total.
+     box-sizing: border-box is inherited, so children sit in 500px. */
+  max-width: 564px;
+  /* 32px padding is an invisible hover buffer so the divider does not
+     immediately snap back when the pointer grazes the visible box
+     edge. Negative top/bottom margins collapse the padding out of
+     layout flow so the demo keeps its rhythm in the page. */
+  padding: 32px;
+  margin: -32px 0 calc(clamp(2rem, 4vw, 3rem) - 32px);
+}
+
+.split-container {
+  position: relative;
+  width: 100%;
+  height: 360px;
+  border-radius: 12px;
+  overflow: hidden;
+  /* Match the page background so the 32px padding area is invisible.
+     The border + border-radius define the visible box; individual
+     demo content provides its own colors on top. */
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  cursor: ew-resize;
+  user-select: none;
+}
+
+.split-before,
+.split-after {
+  position: absolute;
+  inset: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.split-before {
+  z-index: 1;
+}
+
+.split-content {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  /* No padding: demos that fill the container (overdrive) need full
+     bleed, and demos with smaller content (polish, bolder) already
+     center themselves via flex. */
+}
+
+.split-after {
+  clip-path: polygon(58% 0%, 100% 0%, 100% 100%, 42% 100%);
+  z-index: 2;
+  background: var(--color-paper);
+}
+
+.split-divider {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 50%;
+  width: 3px;
+  background: var(--color-accent);
+  transform: translateX(-50%) skewX(-10deg);
+  pointer-events: none;
+  z-index: 3;
+  box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
+}
+
+/* Before | caption | After all on one row. The caption lives in the
+   middle column; if a skill has no caption we emit an empty <span> so
+   the grid still has three cells and Before/After sit at the edges. */
+.split-labels {
+  display: grid;
+  grid-template-columns: auto minmax(0, 1fr) auto;
+  align-items: baseline;
+  gap: var(--spacing-md);
+  margin-top: 10px;
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.12em;
+  color: var(--color-ash);
+}
+
+.split-label-item[data-point="before"] {
+  color: var(--color-ash);
+  justify-self: start;
+}
+
+.split-label-item[data-point="after"] {
+  color: var(--color-accent);
+  justify-self: end;
+}
+
+.skill-demo-caption {
+  margin: 0;
+  font-family: var(--font-body);
+  font-size: 0.8125rem;
+  font-weight: 400;
+  text-transform: none;
+  letter-spacing: 0;
+  color: var(--color-ash);
+  font-style: italic;
+  text-align: center;
+  justify-self: center;
+  /* Allow wrapping inside the middle column if the text is long. */
+  max-width: 100%;
+}
+
+.skill-demo-eyebrow {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.14em;
+  color: var(--color-ash);
+  margin-bottom: var(--spacing-sm);
+  /* Eyebrow now lives inside .split-comparison (which has 32px
+     padding), so it aligns with the visible card edge automatically. */
+}
+
+/* ============================================
+   SKILL DETAIL
+   ============================================ */
+
+/* Editorial hero: on wide viewports, text header on the left and the
+   before/after demo floats to the right as a hero module and is allowed
+   to break out of the 720px body cap. On narrow viewports everything
+   stacks within the 720px column. */
+.skill-detail-hero {
+  max-width: 720px;
+  margin-bottom: clamp(2.5rem, 5vw, 3.5rem);
+}
+
+/* Stacked spacing between header and demo on narrow viewports; cancelled
+   when the hero switches to two columns at >=1280px. */
+.skill-detail-hero--has-demo .skill-demo {
+  margin-top: clamp(2rem, 4vw, 2.5rem);
+}
+
+@media (min-width: 1280px) {
+  .skill-detail-hero--has-demo {
+    /* Wider editorial hero at large viewports. Fixed 564px demo column
+       (500px visible + 64px hover buffer) keeps the split-container at
+       its designed 500x360 landscape aspect ratio. Text column takes
+       whatever's left, clamped by the hero's own max-width. */
+    max-width: 1200px;
+    display: grid;
+    grid-template-columns: minmax(0, 1fr) 564px;
+    gap: clamp(var(--spacing-xl), 4vw, var(--spacing-2xl));
+    align-items: center;
+  }
+
+  .skill-detail-hero--has-demo .skill-detail-header {
+    margin-bottom: 0;
+  }
+
+  .skill-detail-hero--has-demo .skill-demo {
+    align-self: center;
+    margin-top: 0;
+  }
+}
+
+/* Body sections stay at a readable line length regardless of the hero
+   width above them. */
+.skill-detail-editorial,
+.skill-source-card,
+.skill-references {
+  max-width: 720px;
+}
+
+.skill-detail-eyebrow {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  font-weight: 500;
+  text-transform: uppercase;
+  letter-spacing: 0.12em;
+  color: var(--color-ash);
+  margin-bottom: var(--spacing-sm);
+}
+
+.skill-detail-eyebrow a {
+  color: inherit;
+  text-decoration: none;
+}
+
+.skill-detail-eyebrow a:hover {
+  color: var(--color-accent);
+}
+
+.skill-detail-title {
+  font-family: var(--font-display);
+  font-size: clamp(3rem, 5.5vw, 5rem);
+  font-weight: 600;
+  line-height: 0.95;
+  letter-spacing: -0.02em;
+  color: var(--color-ink);
+  margin-bottom: var(--spacing-md);
+  white-space: nowrap;
+}
+
+.skill-detail-title-slash {
+  color: var(--color-accent);
+  font-weight: 300;
+}
+
+.skill-detail-tagline {
+  font-family: var(--font-body);
+  font-size: clamp(1rem, 1.4vw, 1.125rem);
+  font-weight: 400;
+  color: var(--color-charcoal);
+  line-height: 1.55;
+  max-width: 60ch;
+  margin-bottom: var(--spacing-md);
+}
+
+.skill-meta-strip {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  margin-top: var(--spacing-sm);
+}
+
+.skill-meta-chip {
+  display: inline-flex;
+  align-items: center;
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  padding: 4px 10px;
+  border-radius: 99px;
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  color: var(--color-charcoal);
+}
+
+.skill-meta-category[data-category="create"] {
+  background: var(--cat-create-bg);
+  border-color: var(--cat-create-border);
+  color: var(--cat-create-text);
+}
+
+.skill-meta-category[data-category="evaluate"] {
+  background: var(--cat-evaluate-bg);
+  border-color: var(--cat-evaluate-border);
+  color: var(--cat-evaluate-text);
+}
+
+.skill-meta-category[data-category="refine"] {
+  background: var(--cat-refine-bg);
+  border-color: var(--cat-refine-border);
+  color: var(--cat-refine-text);
+}
+
+.skill-meta-category[data-category="simplify"] {
+  background: var(--cat-simplify-bg);
+  border-color: var(--cat-simplify-border);
+  color: var(--cat-simplify-text);
+}
+
+.skill-meta-category[data-category="harden"] {
+  background: var(--cat-harden-bg);
+  border-color: var(--cat-harden-border);
+  color: var(--cat-harden-text);
+}
+
+.skill-meta-category[data-category="system"] {
+  background: var(--cat-system-bg);
+  border-color: var(--cat-system-border);
+  color: var(--cat-system-text);
+}
+
+.skill-meta-args {
+  font-family: var(--font-mono);
+  text-transform: none;
+  letter-spacing: 0;
+  font-weight: 500;
+}
+
+.skill-detail-editorial {
+  margin-bottom: clamp(2rem, 4vw, 3rem);
+}
+
+/* "The skill itself" card: visually contains the auto-rendered SKILL.md
+   body so it reads as a distinct reference block, not a continuation of
+   the editorial section above. */
+.skill-source-card {
+  background: var(--color-paper);
+  border: 1px solid var(--color-mist);
+  border-radius: 12px;
+  padding: clamp(var(--spacing-md), 3vw, var(--spacing-xl));
+  margin-top: clamp(2rem, 4vw, 3rem);
+  box-shadow: 0 1px 0 oklch(90% 0 0);
+}
+
+.skill-source-card-header {
+  display: flex;
+  align-items: baseline;
+  flex-wrap: wrap;
+  gap: var(--spacing-sm);
+  padding-bottom: var(--spacing-md);
+  margin-bottom: var(--spacing-md);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.skill-source-card-label {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  color: var(--color-accent);
+  padding: 3px 8px;
+  background: var(--color-accent-dim);
+  border-radius: 4px;
+  flex-shrink: 0;
+}
+
+.skill-source-card-subtitle {
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+  font-style: italic;
+}
+
+.skill-source-card-body {
+  max-width: none;
+}
+
+.skill-source-card-body > :first-child {
+  margin-top: 0;
+}
+
+.skill-source-card-body > :last-child {
+  margin-bottom: 0;
+}
+
+.skill-references {
+  margin-top: clamp(3rem, 6vw, 4.5rem);
+  padding-top: clamp(2rem, 4vw, 3rem);
+  border-top: 1px solid var(--color-mist);
+}
+
+.skill-references-heading {
+  font-family: var(--font-display);
+  font-size: 1.5rem;
+  font-style: italic;
+  font-weight: 500;
+  color: var(--color-ink);
+  margin-bottom: var(--spacing-md);
+}
+
+.skill-reference {
+  border-top: 1px solid var(--color-mist);
+}
+
+.skill-reference:last-child {
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.skill-reference > summary {
+  list-style: none;
+  cursor: pointer;
+  padding: 16px 0;
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+  transition: color var(--duration-fast) var(--ease-out);
+}
+
+.skill-reference > summary::-webkit-details-marker {
+  display: none;
+}
+
+.skill-reference > summary::before {
+  content: "+";
+  flex-shrink: 0;
+  width: 18px;
+  font-family: var(--font-display);
+  font-size: 1.5rem;
+  color: var(--color-accent);
+  line-height: 1;
+  transition: transform var(--duration-base) var(--ease-out);
+}
+
+.skill-reference[open] > summary::before {
+  transform: rotate(45deg);
+}
+
+.skill-reference > summary:hover {
+  color: var(--color-accent);
+}
+
+.skill-reference-label {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.12em;
+  color: var(--color-ash);
+  flex-shrink: 0;
+}
+
+.skill-reference-title {
+  font-family: var(--font-display);
+  font-size: 1.125rem;
+  font-style: italic;
+  color: var(--color-ink);
+}
+
+.skill-reference-body {
+  padding: var(--spacing-sm) 0 var(--spacing-md) 34px;
+  max-width: 62ch;
+}
+
+/* ============================================
+   PROSE — rendered markdown bodies
+   ============================================ */
+
+.prose {
+  font-size: 1rem;
+  line-height: 1.7;
+  color: var(--color-charcoal);
+  max-width: 65ch;
+}
+
+.prose h1,
+.prose h2,
+.prose h3,
+.prose h4 {
+  color: var(--color-ink);
+  font-weight: 600;
+  scroll-margin-top: 5rem;
+  line-height: 1.25;
+  margin-top: 2em;
+  margin-bottom: 0.6em;
+}
+
+.prose h1 { font-family: var(--font-display); font-size: 1.875rem; font-weight: 500; font-style: italic; }
+.prose h2 { font-family: var(--font-display); font-size: 1.5rem; font-weight: 500; font-style: italic; margin-top: 2.2em; }
+.prose h3 { font-size: 1.125rem; margin-top: 1.8em; }
+.prose h4 { font-size: 1rem; }
+
+.prose h2:first-child,
+.prose h3:first-child {
+  margin-top: 0;
+}
+
+.prose p {
+  margin-top: 0;
+  margin-bottom: 1.1em;
+}
+
+.prose ul,
+.prose ol {
+  margin: 0 0 1.2em 0;
+  padding-left: 1.25rem;
+}
+
+.prose li {
+  margin-bottom: 0.4em;
+}
+
+.prose a {
+  color: var(--color-accent);
+  text-decoration: underline;
+  text-decoration-thickness: 1px;
+  text-underline-offset: 3px;
+  text-decoration-color: var(--color-accent-dim);
+  transition: text-decoration-color var(--duration-fast) var(--ease-out);
+}
+
+.prose a:hover {
+  text-decoration-color: var(--color-accent);
+}
+
+.prose strong {
+  color: var(--color-ink);
+  font-weight: 600;
+}
+
+.prose em {
+  font-style: italic;
+}
+
+.prose code {
+  font-family: var(--font-mono);
+  font-size: 0.875em;
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  padding: 2px 6px;
+  border-radius: 4px;
+  color: var(--color-ink);
+}
+
+.prose .code-block-wrap {
+  position: relative;
+  margin: 1.25em 0;
+}
+
+.prose .code-block-wrap .code-block {
+  margin: 0;
+}
+
+.prose .code-block {
+  padding: var(--spacing-md);
+  background: oklch(12% 0.005 350);
+  color: oklch(92% 0.005 350);
+  border-radius: 10px;
+  overflow-x: auto;
+  font-family: var(--font-mono);
+  font-size: 0.8125rem;
+  line-height: 1.55;
+  border: 1px solid oklch(20% 0.005 350);
+}
+
+.code-block-copy {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  padding: 4px 10px;
+  background: oklch(20% 0.005 350);
+  border: 1px solid oklch(30% 0.005 350);
+  color: oklch(78% 0.005 350);
+  border-radius: 4px;
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  cursor: pointer;
+  opacity: 0;
+  transition:
+    opacity var(--duration-fast) var(--ease-out),
+    background var(--duration-fast) var(--ease-out),
+    color var(--duration-fast) var(--ease-out),
+    border-color var(--duration-fast) var(--ease-out);
+}
+
+.code-block-wrap:hover .code-block-copy,
+.code-block-copy:focus-visible {
+  opacity: 1;
+}
+
+.code-block-copy::before {
+  content: "Copy";
+}
+
+.code-block-copy:hover {
+  background: oklch(30% 0.005 350);
+  color: oklch(92% 0.005 350);
+  border-color: oklch(40% 0.005 350);
+}
+
+.code-block-copy.is-copied {
+  opacity: 1;
+  background: var(--color-accent);
+  color: var(--color-paper);
+  border-color: var(--color-accent);
+}
+
+.code-block-copy.is-copied::before {
+  content: "Copied";
+}
+
+.prose .code-block code {
+  background: transparent;
+  border: none;
+  padding: 0;
+  color: inherit;
+  font-size: inherit;
+}
+
+.prose blockquote {
+  margin: 1.5em 0;
+  padding: 0 0 0 var(--spacing-md);
+  border-left: 3px solid var(--color-mist);
+  color: var(--color-ash);
+  font-style: italic;
+}
+
+.prose hr {
+  border: none;
+  height: 1px;
+  background: var(--color-mist);
+  margin: 2.5em 0;
+}

public/css/tokens.css 🔗

@@ -0,0 +1,102 @@
+/*
+ * impeccable.style — design tokens + minimal reset
+ *
+ * Shared by every page on the site (landing + generated sub-pages).
+ * Keep this file small and dependency-free.
+ */
+
+/* ============================================
+   MINIMAL CSS RESET
+   ============================================ */
+
+*, *::before, *::after {
+  box-sizing: border-box;
+}
+
+* {
+  margin: 0;
+}
+
+img, picture, video, canvas, svg {
+  display: block;
+  max-width: 100%;
+}
+
+button, input, textarea, select {
+  font: inherit;
+}
+
+/* ============================================
+   DESIGN TOKENS
+   ============================================ */
+
+:root {
+  /* Typography */
+  --font-display: 'Cormorant Garamond', Georgia, serif;
+  --font-body: 'Instrument Sans', system-ui, sans-serif;
+  --font-mono: 'Space Grotesk', monospace;
+
+  /* Spacing Scale */
+  --spacing-xs: 8px;
+  --spacing-sm: 16px;
+  --spacing-md: 24px;
+  --spacing-lg: 32px;
+  --spacing-xl: 48px;
+  --spacing-2xl: 80px;
+  --spacing-3xl: 120px;
+
+  /* Width */
+  --width-max: 1400px;
+  --width-content: 900px;
+
+  /* Animation */
+  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
+  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
+  --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+  --duration-fast: 0.15s;
+  --duration-base: 0.3s;
+  --duration-slow: 0.6s;
+  --duration-slower: 0.8s;
+  --duration-slowest: 1.2s;
+
+  /* Core Colors - Light Mode */
+  --color-ink: oklch(10% 0 0);
+  --color-text: oklch(10% 0 0);
+  --color-paper: oklch(98% 0 0);
+  --color-cream: oklch(96% 0.005 350);
+  --color-charcoal: oklch(25% 0 0);
+  --color-ash: oklch(55% 0 0);
+  --color-mist: oklch(92% 0 0);
+  --color-bg: oklch(96% 0.005 350);
+
+  /* Accent - Vibrant Magenta/Rose (original) */
+  --color-accent: oklch(60% 0.25 350);
+  --color-accent-hover: oklch(52% 0.25 350);
+  --color-accent-dim: oklch(60% 0.25 350 / 0.15);
+  --color-accent-soft: oklch(60% 0.25 350 / 0.25);
+
+  /* Framework category colors - Light mode */
+  --cat-create-bg: #fdf2f8;
+  --cat-create-border: #ec4899;
+  --cat-create-text: #be185d;
+
+  --cat-evaluate-bg: #fdf4ff;
+  --cat-evaluate-border: #d946ef;
+  --cat-evaluate-text: #a21caf;
+
+  --cat-refine-bg: #eff6ff;
+  --cat-refine-border: #3b82f6;
+  --cat-refine-text: #1d4ed8;
+
+  --cat-simplify-bg: #fffbeb;
+  --cat-simplify-border: #f59e0b;
+  --cat-simplify-text: #b45309;
+
+  --cat-harden-bg: #f0fdf4;
+  --cat-harden-border: #22c55e;
+  --cat-harden-text: #15803d;
+
+  --cat-system-bg: #f5f5f4;
+  --cat-system-border: #78716c;
+  --cat-system-text: #44403c;
+}

public/css/workflow.css 🔗

@@ -0,0 +1,1347 @@
+/* Workflow / Commands - Renaissance Edition */
+
+.commands-section {
+  position: relative;
+  padding: var(--spacing-xl) 0;
+  background: var(--color-paper);
+}
+
+.commands-gallery {
+  /* Reset grid */
+  display: block;
+}
+
+/* ============================================
+   MAGAZINE SPREAD - Desktop Layout
+   ============================================ */
+
+/* Category accent colors */
+.magazine-spread[data-category="create"] { --spread-accent: oklch(55% 0.20 340); }
+.magazine-spread[data-category="evaluate"] { --spread-accent: oklch(55% 0.20 300); }
+.magazine-spread[data-category="refine"] { --spread-accent: oklch(55% 0.18 260); }
+.magazine-spread[data-category="simplify"] { --spread-accent: oklch(55% 0.20 80); }
+.magazine-spread[data-category="harden"] { --spread-accent: oklch(55% 0.20 160); }
+.magazine-spread[data-category="system"] { --spread-accent: oklch(45% 0.05 0); }
+
+.magazine-container {
+  position: relative;
+  width: 100%;
+  display: grid;
+  grid-template-columns: 200px 1fr;
+  gap: var(--spacing-lg);
+  align-items: start;
+  overflow: hidden;
+}
+
+@media (min-width: 1100px) {
+  .magazine-container {
+    background: white;
+    border-radius: 16px;
+    padding: var(--spacing-lg);
+  }
+}
+
+/* Fisheye command list */
+.fisheye-list {
+  position: relative;
+  height: 400px;
+  align-self: center;
+  overflow: hidden;
+  mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%);
+  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%);
+}
+
+.fisheye-scroll {
+  height: 100%;
+  overflow-y: auto;
+  scrollbar-width: none;
+  position: relative;
+}
+
+.fisheye-scroll::-webkit-scrollbar { display: none; }
+
+.fisheye-item {
+  display: block;
+  font-family: var(--font-display);
+  font-size: 1.5rem;
+  font-weight: 400;
+  color: var(--color-ink);
+  background: none;
+  border: none;
+  padding: 0;
+  position: absolute;
+  left: 0;
+  cursor: pointer;
+  white-space: nowrap;
+  text-align: left;
+  width: 100%;
+  line-height: 1.3;
+  transform-origin: left center;
+  will-change: transform, opacity;
+}
+
+.fisheye-slash {
+  color: var(--color-mist);
+  font-weight: 300;
+}
+
+.fisheye-item.is-active {
+  font-weight: 600;
+}
+
+.fisheye-item.is-active .fisheye-slash {
+  color: var(--color-accent);
+}
+
+
+.fisheye-beta {
+  font-family: var(--font-body);
+  font-size: 0.45rem;
+  font-weight: 600;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: var(--color-ash);
+  border: 1px solid var(--color-mist);
+  border-radius: 3px;
+  padding: 1px 4px;
+  margin-left: 0.35em;
+  vertical-align: middle;
+}
+
+/* The viewport that clips to one spread at a time */
+.magazine-viewport {
+  position: relative;
+  height: 520px;
+  overflow: hidden;
+}
+
+/* Individual spread */
+.magazine-spread {
+  position: absolute;
+  inset: 0;
+  display: grid;
+  grid-template-columns: 38% 1fr;
+  grid-template-rows: 1fr auto;
+  gap: 0 var(--spacing-2xl);
+  padding: var(--spacing-lg) var(--spacing-lg);
+  opacity: 0;
+  pointer-events: none;
+  transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
+              transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
+  transform: translateY(16px);
+  will-change: opacity, transform;
+}
+
+.magazine-spread.active {
+  opacity: 1;
+  pointer-events: auto;
+  transform: translateY(0);
+  z-index: 2;
+}
+
+.magazine-spread.exiting {
+  opacity: 0;
+  transform: translateY(-16px);
+  z-index: 1;
+}
+
+/* Left column: Big name + meta */
+.spread-identity {
+  grid-column: 1;
+  grid-row: 1 / 3;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  gap: var(--spacing-md);
+  padding-right: var(--spacing-lg);
+}
+
+.spread-category-label {
+  font-family: var(--font-body);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.14em;
+  color: var(--spread-accent);
+}
+
+.spread-command-name {
+  white-space: nowrap;
+  font-family: var(--font-display);
+  font-size: clamp(3rem, 5.5vw, 5.5rem);
+  font-weight: 600;
+  line-height: 0.95;
+  color: var(--color-ink);
+  margin: 0;
+  letter-spacing: -0.02em;
+}
+
+.spread-command-name .spread-slash {
+  color: var(--spread-accent);
+  font-weight: 300;
+}
+
+.spread-command-name .beta-badge {
+  font-family: var(--font-body);
+  font-size: 0.55rem;
+  font-weight: 600;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: var(--spread-accent);
+  border: 1px solid var(--spread-accent);
+  border-radius: 3px;
+  padding: 2px 6px;
+  vertical-align: super;
+  margin-left: 8px;
+}
+
+.spread-description {
+  font-family: var(--font-body);
+  font-size: 0.9375rem;
+  line-height: 1.6;
+  color: var(--color-charcoal);
+  max-width: 360px;
+}
+
+/* Flow / relationship line */
+.spread-flow {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  flex-wrap: nowrap;
+  margin-top: var(--spacing-xs);
+  white-space: nowrap;
+}
+
+.spread-flow-label {
+  font-family: var(--font-body);
+  font-size: 0.75rem;
+  color: var(--color-ash);
+}
+
+.spread-flow-icon {
+  color: var(--spread-accent);
+  font-weight: 600;
+  font-size: 0.8125rem;
+}
+
+.spread-flow-cmd {
+  font-family: var(--font-mono);
+  font-size: 0.6875rem;
+  background: var(--color-mist);
+  padding: 2px 7px;
+  border-radius: 3px;
+  color: var(--color-ink);
+}
+
+/* Right column: Demo area */
+.spread-demo-area {
+  grid-column: 2;
+  grid-row: 1 / 3;
+  display: flex;
+  flex-direction: column;
+  min-height: 0;
+  overflow: hidden;
+}
+
+/* Demo split comparison inside the spread */
+.spread-demo-area .demo-split-comparison {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  flex: 1;
+  min-height: 0;
+}
+
+.spread-demo-area .demo-split-comparison .split-container {
+  position: relative;
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+  cursor: ew-resize;
+  user-select: none;
+  background: var(--color-cream);
+}
+
+.spread-demo-area .demo-split-comparison .split-before,
+.spread-demo-area .demo-split-comparison .split-after {
+  position: absolute;
+  inset: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: var(--spacing-md);
+}
+
+.spread-demo-area .demo-split-comparison .split-before {
+  z-index: 1;
+  background: var(--color-cream);
+}
+
+.spread-demo-area .demo-split-comparison .split-after {
+  z-index: 2;
+  background: var(--color-paper);
+  clip-path: polygon(58% 0%, 100% 0%, 100% 100%, 42% 100%);
+}
+
+.spread-demo-area .demo-split-comparison .split-content {
+  width: 100%;
+  max-width: 320px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.spread-demo-area .demo-split-comparison .split-divider {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 50%;
+  width: 2px;
+  background: var(--spread-accent, var(--color-accent));
+  transform: translateX(-50%) skewX(-10deg);
+  pointer-events: none;
+  z-index: 3;
+  box-shadow: 0 0 12px rgba(0,0,0,0.1);
+}
+
+.spread-demo-area .demo-split-comparison .split-label {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%) skewX(10deg);
+  font-size: 0.5625rem;
+  font-weight: 600;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: var(--color-paper);
+  background: var(--spread-accent, var(--color-accent));
+  padding: 4px 10px;
+  border-radius: 3px;
+  white-space: nowrap;
+}
+
+.spread-demo-area .demo-split-comparison .demo-caption {
+  flex-shrink: 0;
+  font-size: 0.75rem;
+  color: var(--color-ash);
+  text-align: center;
+  padding: var(--spacing-sm) var(--spacing-md);
+}
+
+/* Fallback demo container inside spread */
+.spread-demo-area .demo-container {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
+
+.spread-demo-area .demo-container .demo-viewport {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  max-width: 420px;
+  background: none;
+  min-height: auto;
+}
+
+/* (nav moved to fisheye list) */
+
+/* ============================================
+   OLD DESKTOP STYLES (kept for mobile reuse)
+   ============================================ */
+
+.commands-container {
+  display: grid;
+  grid-template-columns: 1fr 1.2fr;
+  gap: var(--spacing-2xl);
+  align-items: start;
+}
+
+@media (max-width: 900px) {
+  .commands-container {
+    grid-template-columns: 1fr;
+  }
+}
+
+/* Left: The Manual (Scrollable) */
+.command-manual {
+  display: flex;
+  flex-direction: column;
+  gap: var(--spacing-sm);
+  padding-bottom: 20vh;
+}
+
+.command-category-header {
+  font-family: var(--font-display);
+  font-size: 0.875rem;
+  font-weight: 600;
+  color: var(--color-accent);
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+  padding: var(--spacing-lg) var(--spacing-lg) var(--spacing-sm);
+  margin-top: var(--spacing-md);
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.command-category-header:first-child {
+  margin-top: 0;
+}
+
+.manual-entry {
+  position: relative;
+  padding: var(--spacing-lg);
+  padding-left: calc(var(--spacing-lg) + 16px); /* Reserve space for transform */
+  border-left: 2px solid var(--color-mist);
+  transition: border-color 0.4s var(--ease-out), opacity 0.4s var(--ease-out), background 0.4s var(--ease-out), transform 0.4s var(--ease-out);
+  opacity: 0.4;
+  cursor: pointer;
+  transform: translateX(-16px);
+}
+
+.manual-entry:hover {
+  opacity: 0.7;
+}
+
+.manual-entry.active {
+  border-left-color: var(--color-accent);
+  opacity: 1;
+  transform: translateX(0);
+  background: linear-gradient(to right, var(--color-bg), transparent);
+}
+
+.manual-cmd-name {
+  font-family: var(--font-mono);
+  font-size: 1.5rem;
+  margin: 0 0 var(--spacing-sm);
+  color: var(--color-ink);
+  font-weight: 500;
+}
+
+.beta-badge {
+  font-family: var(--font-body);
+  font-size: 0.55rem;
+  font-weight: 600;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: var(--color-accent);
+  border: 1px solid var(--color-accent);
+  border-radius: 3px;
+  padding: 1px 5px;
+  vertical-align: middle;
+  margin-left: 6px;
+}
+
+.manual-cmd-desc {
+  font-size: 0.875rem;
+  line-height: 1.5;
+  color: var(--color-charcoal);
+  margin: 0;
+}
+
+.manual-cmd-rel {
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+  margin-top: var(--spacing-sm);
+  display: flex;
+  align-items: center;
+  gap: 0.5ch;
+  flex-wrap: wrap;
+}
+
+.manual-cmd-rel .rel-icon {
+  color: var(--color-accent);
+  font-weight: 600;
+}
+
+.manual-cmd-rel code {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  background: var(--color-mist);
+  padding: 2px 6px;
+  border-radius: 3px;
+  color: var(--color-ink);
+}
+
+/* Right: The Terminal Stack (Sticky, vertically centered) */
+.glass-terminal-wrapper {
+  position: sticky;
+  top: 50vh;
+  transform: translateY(-50%);
+  height: calc(100vh - var(--spacing-xl) * 2);
+  max-height: 800px;
+  min-height: 500px;
+}
+
+/* Stacked Windows Container */
+.terminal-stack {
+  position: relative;
+  height: 100%;
+  perspective: 1200px;
+}
+
+.terminal-stack-tabs {
+  position: absolute;
+  top: -31px;
+  right: 8px;
+  display: flex;
+  gap: 4px;
+  z-index: 10;
+}
+
+.terminal-stack-tab {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  padding: 5px 12px;
+  background: var(--color-cream);
+  border: 1px solid var(--color-mist);
+  border-bottom: none;
+  border-radius: 6px 6px 0 0;
+  color: var(--color-ash);
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.terminal-stack-tab:hover {
+  background: var(--color-paper);
+  color: var(--color-charcoal);
+}
+
+.terminal-stack-tab.active {
+  background: var(--color-paper);
+  color: var(--color-ink);
+  border-color: var(--color-mist);
+}
+
+/* Individual Windows */
+.terminal-window {
+  position: absolute;
+  inset: 0;
+  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1),
+              opacity 0.3s ease,
+              filter 0.3s ease;
+  transform-origin: center bottom;
+}
+
+/* Demo window (front by default) */
+.terminal-window--demo {
+  z-index: 2;
+}
+
+.terminal-window--demo.is-back {
+  transform: translateY(16px) translateX(12px) scale(0.96);
+  opacity: 0.6;
+  filter: brightness(0.92);
+  pointer-events: none;
+  z-index: 1;
+}
+
+/* Source window (back by default) */
+.terminal-window--source {
+  z-index: 1;
+  transform: translateY(16px) translateX(12px) scale(0.96);
+  opacity: 0.6;
+  filter: brightness(0.92);
+  pointer-events: none;
+}
+
+.terminal-window--source.is-front {
+  transform: translateY(0) translateX(0) scale(1);
+  opacity: 1;
+  filter: brightness(1);
+  pointer-events: auto;
+  z-index: 2;
+}
+
+/* Source Window Content */
+.source-window {
+  background: var(--color-paper);
+  backdrop-filter: blur(12px);
+  -webkit-backdrop-filter: blur(12px);
+  border: 1px solid var(--color-mist);
+  border-radius: 8px;
+  box-shadow: 0 20px 60px -10px rgba(0,0,0,0.15);
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+.source-header {
+  background: var(--color-cream);
+  padding: 12px 16px;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  border-bottom: 1px solid var(--color-mist);
+  flex-shrink: 0;
+}
+
+.source-title {
+  font-family: var(--font-mono);
+  font-size: 0.875rem;
+  color: var(--color-ink);
+  font-weight: 500;
+}
+
+.source-body {
+  flex: 1;
+  padding: var(--spacing-md);
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  line-height: 1.5;
+  color: var(--color-charcoal);
+  overflow-y: auto;
+  overscroll-behavior: contain;
+  white-space: pre-wrap;
+  word-break: break-word;
+  background: var(--color-cream);
+}
+
+.source-loading {
+  color: var(--color-ash);
+  font-style: italic;
+}
+
+@media (max-width: 900px) {
+  .magazine-container { display: none; }
+  .fisheye-list { display: none; }
+  .glass-terminal-wrapper {
+    display: none; /* Hide on mobile for now, or stack */
+  }
+}
+
+.glass-terminal {
+  background: var(--color-paper);
+  backdrop-filter: blur(12px);
+  -webkit-backdrop-filter: blur(12px);
+  border: 1px solid var(--color-mist);
+  border-radius: 8px;
+  box-shadow: 0 20px 60px -10px rgba(0,0,0,0.15);
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+.terminal-header {
+  background: var(--color-cream);
+  padding: 12px 16px;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.terminal-dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+}
+.terminal-dot.red { background: #ff5f56; }
+.terminal-dot.yellow { background: #ffbd2e; }
+.terminal-dot.green { background: #27c93f; }
+
+.terminal-title {
+  margin-left: auto;
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  color: var(--color-ash);
+}
+
+.terminal-body {
+  flex: 1;
+  padding: var(--spacing-md);
+  font-family: var(--font-mono);
+  font-size: 0.9375rem;
+  color: var(--color-ink);
+  overflow-y: auto;
+  display: flex;
+  flex-direction: column;
+  min-height: 0; /* Allow flex shrinking */
+}
+
+.terminal-line {
+  margin-bottom: var(--spacing-sm);
+  display: flex;
+  gap: var(--spacing-sm);
+  line-height: 1.5;
+}
+
+.terminal-prompt {
+  color: var(--color-accent);
+  user-select: none;
+  font-weight: bold;
+}
+
+.terminal-cursor {
+  display: inline-block;
+  width: 8px;
+  height: 1.2em;
+  background: var(--color-accent);
+  vertical-align: middle;
+  animation: blink 1s step-end infinite;
+}
+
+.terminal-output {
+  color: var(--color-ash);
+  margin-bottom: var(--spacing-md);
+  white-space: pre-wrap;
+}
+
+/* Hide steps when viewport is short */
+@media (max-height: 800px) {
+  .terminal-output {
+    display: none;
+  }
+}
+
+.terminal-cmd {
+  color: var(--color-accent);
+  font-weight: 600;
+}
+
+.terminal-step {
+  color: var(--color-charcoal);
+}
+
+.terminal-done {
+  color: var(--color-success, #22c55e);
+  font-weight: 500;
+}
+
+.terminal-preview {
+  background: var(--color-paper);
+  margin: var(--spacing-sm) 0;
+  flex: 1;
+  min-height: 0; /* Allow shrinking */
+  overflow: hidden;
+  border-radius: 12px;
+}
+
+.terminal-cursor-line {
+  flex-shrink: 0;
+  margin-top: var(--spacing-sm) !important;
+}
+
+/* Demo Split Comparison in Terminal */
+.terminal-preview .demo-split-comparison {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+.terminal-preview .demo-split-comparison .split-container {
+  position: relative;
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+  cursor: ew-resize;
+  user-select: none;
+  background: var(--color-cream);
+}
+
+.terminal-preview .demo-split-comparison .split-before,
+.terminal-preview .demo-split-comparison .split-after {
+  position: absolute;
+  inset: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: var(--spacing-md);
+}
+
+.terminal-preview .demo-split-comparison .split-before {
+  z-index: 1;
+  background: var(--color-cream);
+}
+
+.terminal-preview .demo-split-comparison .split-after {
+  z-index: 2;
+  background: var(--color-paper);
+  clip-path: polygon(58% 0%, 100% 0%, 100% 100%, 42% 100%);
+}
+
+.terminal-preview .demo-split-comparison .split-content {
+  width: 100%;
+  max-width: 280px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.terminal-preview .demo-split-comparison .split-divider {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 50%;
+  width: 2px;
+  background: var(--color-accent);
+  transform: translateX(-50%) skewX(-10deg);
+  pointer-events: none;
+  z-index: 3;
+  box-shadow: 0 0 12px rgba(0,0,0,0.1);
+}
+
+.terminal-preview .demo-split-comparison .split-label {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%) skewX(10deg);
+  font-size: 0.5625rem;
+  font-weight: 600;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: var(--color-paper);
+  background: var(--color-accent);
+  padding: 4px 10px;
+  border-radius: 3px;
+  white-space: nowrap;
+}
+
+.terminal-preview .demo-split-comparison .demo-caption {
+  flex-shrink: 0;
+  font-size: 0.75rem;
+  color: var(--color-ash);
+  text-align: center;
+  padding: var(--spacing-sm) var(--spacing-md);
+}
+
+@keyframes blink { 50% { opacity: 0; } }
+
+/* ============================================
+   CASE STUDIES / TRANSFORMATIONS SECTION
+   ============================================ */
+
+.casestudies-section {
+  position: relative;
+  padding: var(--spacing-2xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+/* Tabbed transformations */
+.transformations-tabbed {
+  margin-top: var(--spacing-xl);
+}
+
+.transformation-tabs {
+  display: flex;
+  gap: var(--spacing-xs);
+  border-bottom: 1px solid var(--color-mist);
+  margin-bottom: var(--spacing-lg);
+}
+
+.transformation-tab {
+  font-family: var(--font-display);
+  font-size: 0.9375rem;
+  font-weight: 500;
+  color: var(--color-ash);
+  background: none;
+  border: none;
+  padding: var(--spacing-sm) var(--spacing-md);
+  cursor: pointer;
+  position: relative;
+  transition: color 0.2s ease;
+}
+
+.transformation-tab:hover {
+  color: var(--color-charcoal);
+}
+
+.transformation-tab.active {
+  color: var(--color-ink);
+}
+
+.transformation-tab.active::after {
+  content: '';
+  position: absolute;
+  bottom: -1px;
+  left: 0;
+  right: 0;
+  height: 2px;
+  background: var(--color-accent);
+}
+
+.transformation-panels {
+  position: relative;
+}
+
+.transformation-panel {
+  display: none;
+  flex-direction: column;
+  gap: var(--spacing-lg);
+  animation: fadeInPanel 0.3s ease;
+}
+
+.transformation-panel.active {
+  display: flex;
+}
+
+@keyframes fadeInPanel {
+  from {
+    opacity: 0;
+    transform: translateY(8px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+/* Side-by-side images */
+.transformation-images {
+  display: flex;
+  align-items: center;
+  gap: var(--spacing-md);
+}
+
+.transformation-before,
+.transformation-after {
+  flex: 1;
+  margin: 0;
+}
+
+.transformation-before img,
+.transformation-after img,
+.transformation-placeholder {
+  width: 100%;
+  aspect-ratio: 16 / 10;
+  object-fit: cover;
+  border-radius: 8px;
+  border: 1px solid var(--color-mist);
+  cursor: pointer;
+  transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.transformation-before img:hover,
+.transformation-after img:hover,
+.transformation-placeholder:hover {
+  transform: scale(1.02);
+  box-shadow: 0 8px 24px -4px rgba(0,0,0,0.15);
+}
+
+.transformation-placeholder {
+  background: linear-gradient(135deg, var(--color-mist) 0%, var(--color-cream) 100%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: var(--color-ash);
+  font-size: 0.8125rem;
+  font-style: italic;
+}
+
+.transformation-before figcaption,
+.transformation-after figcaption {
+  font-size: 0.75rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  color: var(--color-ash);
+  margin-top: var(--spacing-xs);
+  text-align: center;
+}
+
+.transformation-arrow {
+  font-size: 1.5rem;
+  color: var(--color-accent);
+  font-weight: 300;
+  flex-shrink: 0;
+}
+
+/* Info section */
+.transformation-info {
+  max-width: 600px;
+}
+
+.transformation-title {
+  font-family: var(--font-display);
+  font-size: 1.25rem;
+  font-weight: 600;
+  color: var(--color-ink);
+  margin: 0 0 var(--spacing-xs);
+}
+
+.transformation-desc {
+  font-size: 0.9375rem;
+  color: var(--color-charcoal);
+  line-height: 1.6;
+  margin: 0 0 var(--spacing-sm);
+}
+
+.transformation-commands {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+}
+
+.transformation-command {
+  font-family: var(--font-mono);
+  font-size: 0.75rem;
+  background: var(--color-mist);
+  color: var(--color-charcoal);
+  padding: 4px 10px;
+  border-radius: 4px;
+}
+
+/* Lightbox */
+.lightbox {
+  position: fixed;
+  inset: 0;
+  background: rgba(0, 0, 0, 0.9);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1000;
+  opacity: 0;
+  visibility: hidden;
+  transition: opacity 0.3s ease, visibility 0.3s ease;
+}
+
+.lightbox.active {
+  opacity: 1;
+  visibility: visible;
+}
+
+.lightbox-close {
+  position: absolute;
+  top: 20px;
+  right: 24px;
+  background: none;
+  border: none;
+  color: white;
+  font-size: 2.5rem;
+  cursor: pointer;
+  opacity: 0.7;
+  transition: opacity 0.2s ease;
+  line-height: 1;
+}
+
+.lightbox-close:hover {
+  opacity: 1;
+}
+
+.lightbox-image {
+  max-width: 90vw;
+  max-height: 85vh;
+  object-fit: contain;
+  border-radius: 8px;
+  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
+}
+
+@media (max-width: 768px) {
+  .transformation-images {
+    flex-direction: column;
+  }
+
+  .transformation-arrow {
+    transform: rotate(90deg);
+  }
+
+  .transformation-before,
+  .transformation-after {
+    width: 100%;
+  }
+}
+
+/* ============================================
+   HERO VERSION LINK
+   ============================================ */
+
+.hero-version-link {
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+  margin-top: var(--spacing-sm);
+}
+
+.hero-version-link a {
+  color: var(--color-ash);
+  text-decoration: none;
+  border-bottom: 1px solid transparent;
+  transition: color 0.2s ease, border-color 0.2s ease;
+}
+
+.hero-version-link a:hover {
+  color: var(--color-accent);
+  border-bottom-color: var(--color-accent);
+}
+
+/* ============================================
+   CHANGELOG + FAQ ROW (side-by-side on desktop)
+   ============================================ */
+
+.changelog-faq-row {
+  position: relative;
+}
+
+@media (min-width: 1024px) {
+  .changelog-faq-row {
+    display: grid;
+    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+    column-gap: calc(var(--spacing-xl) * 1.5);
+    border-top: 1px solid var(--color-mist);
+  }
+
+  .changelog-faq-row > .changelog-section,
+  .changelog-faq-row > .faq-section {
+    border-top: none;
+  }
+
+  .changelog-faq-row > .faq-section {
+    border-left: 1px solid var(--color-mist);
+    padding-left: calc(var(--spacing-xl) * 0.75);
+    margin-left: calc(var(--spacing-xl) * -0.75);
+  }
+}
+
+/* ============================================
+   CHANGELOG SECTION
+   ============================================ */
+
+.changelog-section {
+  position: relative;
+  padding: var(--spacing-xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.changelog-list {
+  display: flex;
+  flex-direction: column;
+  gap: 0;
+}
+
+.changelog-entry {
+  padding: var(--spacing-md) 0;
+}
+
+.changelog-entry + .changelog-entry {
+  border-top: 1px solid var(--color-mist);
+}
+
+.changelog-version-header {
+  display: flex;
+  align-items: baseline;
+  gap: var(--spacing-sm);
+  margin-bottom: var(--spacing-sm);
+}
+
+.changelog-version {
+  font-family: var(--font-mono);
+  font-size: 1.125rem;
+  font-weight: 600;
+  color: var(--color-ink);
+}
+
+.changelog-date {
+  font-size: 0.8125rem;
+  color: var(--color-ash);
+}
+
+.changelog-items {
+  margin: 0;
+  padding-left: var(--spacing-md);
+  color: var(--color-charcoal);
+  line-height: 1.7;
+  max-width: 70ch;
+}
+
+.changelog-items li {
+  margin-bottom: var(--spacing-xs);
+}
+
+.changelog-items code {
+  font-family: var(--font-mono);
+  font-size: 0.875em;
+  background: var(--color-mist);
+  padding: 2px 6px;
+  border-radius: 3px;
+  color: var(--color-ink);
+}
+
+/* Changelog: collapsible older entries */
+.changelog-older {
+  margin-top: var(--spacing-sm);
+}
+
+.changelog-older-toggle {
+  font-family: var(--font-body);
+  font-size: 0.875rem;
+  font-weight: 500;
+  color: var(--color-accent);
+  padding: var(--spacing-md) 0;
+  cursor: pointer;
+  list-style: none;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  transition: color 0.2s ease;
+}
+
+.changelog-older-toggle::-webkit-details-marker {
+  display: none;
+}
+
+.changelog-older-toggle::before {
+  content: '+';
+  font-size: 1.25rem;
+  font-weight: 300;
+  transition: transform 0.3s var(--ease-out);
+}
+
+.changelog-older[open] .changelog-older-toggle::before {
+  transform: rotate(45deg);
+}
+
+.changelog-older-toggle:hover {
+  color: var(--color-accent-hover);
+}
+
+.changelog-older-entries {
+  animation: faqFadeIn 0.3s var(--ease-out);
+}
+
+
+/* ============================================
+   FAQ SECTION
+   ============================================ */
+
+.faq-section {
+  position: relative;
+  padding: var(--spacing-xl) 0;
+  border-top: 1px solid var(--color-mist);
+}
+
+.faq-list {
+  display: flex;
+  flex-direction: column;
+  gap: 0;
+}
+
+.faq-item {
+  border-bottom: 1px solid var(--color-mist);
+}
+
+.faq-question {
+  font-family: var(--font-display);
+  font-size: 1.125rem;
+  font-weight: 500;
+  color: var(--color-ink);
+  padding: var(--spacing-md) 0;
+  cursor: pointer;
+  list-style: none;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  transition: color 0.2s ease;
+}
+
+.faq-question::-webkit-details-marker {
+  display: none;
+}
+
+.faq-question::after {
+  content: '+';
+  font-family: var(--font-body);
+  font-size: 1.5rem;
+  font-weight: 300;
+  color: var(--color-accent);
+  transition: transform 0.3s var(--ease-out);
+}
+
+.faq-item[open] .faq-question::after {
+  transform: rotate(45deg);
+}
+
+.faq-question:hover {
+  color: var(--color-accent);
+}
+
+.faq-answer {
+  padding: 0 0 var(--spacing-md);
+  color: var(--color-charcoal);
+  line-height: 1.7;
+  max-width: 70ch;
+  animation: faqFadeIn 0.3s var(--ease-out);
+}
+
+.faq-answer p {
+  margin: 0 0 var(--spacing-sm);
+}
+
+.faq-answer p:last-child {
+  margin-bottom: 0;
+}
+
+.faq-answer ul {
+  margin: var(--spacing-sm) 0;
+  padding-left: var(--spacing-md);
+}
+
+.faq-answer li {
+  margin-bottom: var(--spacing-xs);
+}
+
+.faq-answer code {
+  font-family: var(--font-mono);
+  font-size: 0.875em;
+  background: var(--color-mist);
+  padding: 2px 6px;
+  border-radius: 3px;
+  color: var(--color-ink);
+}
+
+.faq-answer a {
+  color: var(--color-accent);
+  text-decoration: none;
+  border-bottom: 1px solid transparent;
+  transition: border-color 0.2s ease;
+}
+
+.faq-answer a:hover {
+  border-bottom-color: var(--color-accent);
+}
+
+/* Mobile: FAQ + Changelog */
+@media (max-width: 600px) {
+  .faq-question {
+    font-size: 1rem;
+    padding: var(--spacing-md) 0;
+    min-height: 44px;
+    gap: var(--spacing-sm);
+  }
+
+  .faq-answer {
+    font-size: 0.875rem;
+  }
+
+  .changelog-version {
+    font-size: 1rem;
+  }
+
+  .changelog-version-header {
+    flex-wrap: wrap;
+    gap: var(--spacing-xs);
+  }
+
+  .changelog-items {
+    padding-left: var(--spacing-sm);
+    font-size: 0.875rem;
+  }
+
+  .changelog-older-toggle {
+    min-height: 44px;
+  }
+}
+
+@keyframes faqFadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(-8px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}

public/favicon.svg 🔗

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+  <rect width="32" height="32" rx="6" fill="#1a1a1a"/>
+  <text x="16" y="24" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="500" fill="#f5f3ef" text-anchor="middle">/</text>
+</svg>

public/index.html 🔗

@@ -0,0 +1,734 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <!-- Google tag (gtag.js) -->
+  <script async src="https://www.googletagmanager.com/gtag/js?id=G-TEXGHC7V34"></script>
+  <script>
+    window.dataLayer = window.dataLayer || [];
+    function gtag(){dataLayer.push(arguments);}
+    gtag('js', new Date());
+    gtag('config', 'G-TEXGHC7V34');
+  </script>
+
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Impeccable: The missing upgrade to Anthropic's impeccable skill</title>
+  <meta name="description" content="1 skill, 18 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
+  <meta name="theme-color" content="#fafafa">
+  <link rel="canonical" href="https://impeccable.style">
+
+  <!-- OpenGraph -->
+  <meta property="og:type" content="website">
+  <meta property="og:url" content="https://impeccable.style">
+  <meta property="og:title" content="Impeccable: Design skills for AI harnesses">
+  <meta property="og:description" content="1 skill, 18 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
+  <meta property="og:image" content="https://impeccable.style/og-image.jpg">
+
+  <!-- Twitter -->
+  <meta name="twitter:card" content="summary_large_image">
+  <meta name="twitter:site" content="@pbakaus">
+  <meta name="twitter:creator" content="@pbakaus">
+  <meta name="twitter:title" content="Impeccable: Design skills for AI harnesses">
+  <meta name="twitter:description" content="1 skill, 18 commands, and curated anti-patterns for impeccable frontend design.">
+  <meta name="twitter:image" content="https://impeccable.style/og-image.jpg">
+
+  <link rel="icon" type="image/svg+xml" href="./favicon.svg">
+  <link rel="apple-touch-icon" href="./apple-touch-icon.png">
+  <link rel="preconnect" href="https://fonts.googleapis.com">
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+  <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400&family=Instrument+Sans:wght@400;500;600;700&family=Inter:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
+  <link rel="stylesheet" href="./css/main.css">
+  <link rel="stylesheet" href="./css/sub-pages.css">
+
+  </head>
+<body>
+  <!-- Skip to main content link for keyboard users -->
+  <a href="#main-content" class="skip-link">Skip to main content</a>
+
+  <!-- Grain Overlay -->
+  <div class="grain-overlay" aria-hidden="true"></div>
+
+  <!-- site-header v1 -->
+  <header class="site-header" data-site-header>
+    <a href="/" class="site-header-brand" aria-label="Impeccable home">
+      <svg class="site-header-brand-logo" viewBox="0 0 32 32" aria-hidden="true"><rect width="32" height="32" rx="6" fill="#1a1a1a"/><text x="16" y="24" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="500" fill="#f5f3ef" text-anchor="middle">/</text></svg>
+      <span class="site-header-brand-name">Impeccable</span>
+    </a>
+    <div class="site-header-right">
+      <nav class="site-header-nav" aria-label="Primary">
+        <a href="/" data-nav="home" aria-current="page">Home</a>
+        <a href="/skills" data-nav="docs">Docs</a>
+        <a href="/anti-patterns" data-nav="anti-patterns">Anti-Patterns</a>
+        <a href="/visual-mode" data-nav="visual-mode">Visual Mode</a>
+      </nav>
+      <a href="https://github.com/pbakaus/impeccable" class="site-header-github" target="_blank" rel="noopener" aria-label="Impeccable on GitHub, 18k stars">
+        <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
+        <span class="site-header-github-label">18k</span>
+        <svg class="site-header-github-star" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2l2.76 6.36L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.24-.91L12 2z"/></svg>
+      </a>
+    </div>
+  </header>
+
+  <!-- Sticky Section Nav -->
+  <nav class="section-nav" id="section-nav" aria-label="Page sections">
+    <a href="#foundation" class="section-nav-item" data-section="foundation"><span class="section-nav-num">01</span><span class="section-nav-label">Foundation</span></a>
+    <a href="#language" class="section-nav-item" data-section="language"><span class="section-nav-num">02</span><span class="section-nav-label">Language</span></a>
+    <a href="#antidote" class="section-nav-item" data-section="antidote"><span class="section-nav-num">03</span><span class="section-nav-label">Antidote</span></a>
+    <a href="#visual-mode" class="section-nav-item" data-section="visual-mode"><span class="section-nav-num">04</span><span class="section-nav-label">Visual</span></a>
+    <a href="#downloads" class="section-nav-item" data-section="downloads"><span class="section-nav-num">05</span><span class="section-nav-label">Install</span></a>
+    <a href="#changelog" class="section-nav-item" data-section="changelog"><span class="section-nav-num">06</span><span class="section-nav-label">New</span></a>
+    <a href="#faq" class="section-nav-item" data-section="faq"><span class="section-nav-num">07</span><span class="section-nav-label">FAQ</span></a>
+  </nav>
+
+  <!-- 1. HERO - Combined with Problem Section -->
+  <section id="hero" class="hero-combined">
+    <div class="hero-combined-container">
+      <!-- Left: Title & Info -->
+      <div class="hero-combined-left">
+        <h1 class="hero-title-combined">Impeccable</h1>
+        <p class="hero-tagline-combined">Design fluency for AI harnesses</p>
+
+        <p class="hero-hook-text hero-hook-text--full">Great design prompts require design vocabulary. Most people don't have it. Impeccable teaches your AI deep design knowledge and gives you 18 commands to steer the result.</p>
+        <p class="hero-hook-text hero-hook-text--short">Impeccable teaches your AI real design and gives you 18 commands to steer the result.</p>
+
+        <div class="hero-included-box">
+          <span class="hero-included-title">What's included</span>
+          <div class="hero-included-items">
+            <span>Enhanced <em>impeccable</em> skill + anti-patterns</span>
+            <span class="hero-included-sep">·</span>
+            <span>18 design commands: /polish, /audit, /typeset, /overdrive...</span>
+          </div>
+        </div>
+
+        <div class="hero-cta-group">
+          <a href="#downloads" class="hero-cta-combined">Get Started</a>
+          <div class="hero-logos-inline">
+            <span class="hero-logos-label">Works with</span>
+            <div class="hero-logos-row">
+              <span class="hero-logo-icon has-tooltip" data-tooltip="Cursor"><img src="assets/cursor-logo.png" alt="Cursor" width="24" height="24" loading="lazy"></span>
+              <span class="hero-logo-icon has-tooltip" data-tooltip="Claude Code"><img src="assets/claude-logo.png" alt="Claude Code" width="24" height="24" loading="lazy"></span>
+              <span class="hero-logo-icon has-tooltip" data-tooltip="Gemini CLI"><img src="assets/gemini-logo.png" alt="Gemini CLI" width="20" height="20" loading="lazy"></span>
+              <span class="hero-logo-icon has-tooltip" data-tooltip="Codex CLI"><img src="assets/openai-logo.png" alt="Codex CLI" width="20" height="20" loading="lazy"></span>
+              <span class="hero-logo-icon has-tooltip" data-tooltip="VS Code Copilot"><img src="assets/github-logo.png" alt="VS Code Copilot" width="20" height="20" loading="lazy"></span>
+              <span class="hero-logo-icon has-tooltip" data-tooltip="Antigravity"><img src="assets/antigravity-logo.png" alt="Antigravity" width="20" height="20" loading="lazy"></span>
+              <span class="hero-logo-icon has-tooltip" data-tooltip="Kiro"><img src="assets/kiro-logo.png" alt="Kiro" width="20" height="20" loading="lazy"></span>
+              <span class="hero-logo-icon has-tooltip" data-tooltip="OpenCode"><img src="assets/opencode-logo.png" alt="OpenCode" width="20" height="20" loading="lazy"></span>
+              <span class="hero-logo-icon has-tooltip" data-tooltip="Pi"><img src="assets/pi-logo.svg" alt="Pi" width="20" height="20" loading="lazy" style="background:#1a1a1a;border-radius:50%;padding:3px"></span>
+            </div>
+          </div>
+        </div>
+        <p class="hero-version-link"><a href="#changelog">v2.1: Streamlined from 21 to 18 commands</a></p>
+      </div>
+
+      <!-- Right: Before/After Demo -->
+      <div class="hero-combined-right">
+        <div class="split-comparison" id="lens-comparison">
+          <div class="split-container">
+            <!-- Before: AI Slop -->
+            <div class="split-before">
+              <span class="split-badge split-badge--before">Before</span>
+              <div class="slop-card">
+                <div class="slop-header">
+                  <div class="slop-avatar"></div>
+                  <div class="slop-text">
+                    <div class="slop-title">Welcome to Our Platform</div>
+                    <div class="slop-subtitle">The best solution for your needs</div>
+                  </div>
+                </div>
+                <p class="slop-body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. This is generic placeholder text.</p>
+                <button class="slop-button">Get Started</button>
+              </div>
+              <!-- Callout labels pointing to issues -->
+              <div class="slop-callouts">
+                <span class="slop-callout" data-point="font">Inter font</span>
+                <span class="slop-callout" data-point="gradient">Purple gradient</span>
+                <span class="slop-callout" data-point="copy">Generic copy</span>
+                <span class="slop-callout" data-point="rounded">Cards on cards</span>
+              </div>
+            </div>
+            <!-- After: Impeccable (shown through split) -->
+            <div class="split-after">
+              <span class="split-badge split-badge--after">After</span>
+              <div class="impeccable-card">
+                <p class="impeccable-eyebrow">Introducing</p>
+                <p class="impeccable-title">Thoughtful Design</p>
+                <p class="impeccable-body">Every element serves a purpose. Hierarchy guides the eye. Whitespace breathes.</p>
+                <button class="impeccable-button">Explore</button>
+              </div>
+            </div>
+            <!-- The Divider -->
+            <div class="split-divider"></div>
+          </div>
+          <div class="split-labels">
+            <div class="split-label-item" data-point="before">
+              <span class="split-label-dot"></span>
+              <span>Generic AI Output</span>
+            </div>
+            <div class="split-label-item" data-point="after">
+              <span class="split-label-dot split-label-dot--accent"></span>
+              <span>With Design Skills</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </section>
+
+  <main class="site-content" id="main-content">
+
+    <!-- 01. THE FOUNDATION - Design knowledge -->
+    <section class="foundation-section" id="foundation">
+      <div class="section-header" data-reveal>
+        <span class="section-number">01</span>
+        <h2 class="section-title">The Foundation</h2>
+      </div>
+      <div class="foundation-content">
+        <p class="section-lead" data-reveal>Before commands, before detection, Impeccable teaches your AI real design. Deep reference knowledge across 7 dimensions, loaded every time your AI writes code.</p>
+
+        <div class="foundation-grid" data-reveal>
+          <!-- 7 dimension cards, rendered by JS -->
+        </div>
+
+        <div class="foundation-cta" data-reveal>
+          <p class="foundation-cta-text">Run <code><span class="foundation-slash">/</span>impeccable teach</code> once to set your project's design context. Every command benefits.</p>
+        </div>
+      </div>
+    </section>
+
+    <!-- 02. THE LANGUAGE - Commands vocabulary + demos -->
+    <section class="language-section" id="language">
+      <div id="solution" style="height:0;overflow:hidden;visibility:hidden"></div>
+      <div class="section-header" data-reveal>
+        <span class="section-number">02</span>
+        <h2 class="section-title">The Language</h2>
+      </div>
+      <div class="language-content">
+        <p class="section-lead" data-reveal>18 commands form a shared vocabulary between you and your AI. Each one encodes a specific design discipline, so you can steer with precision.</p>
+
+        <div class="solution-visual-interactive" id="framework-viz-container" data-reveal>
+          <!-- Periodic table generated by JS -->
+        </div>
+
+        <div id="commands-section" style="height:0;overflow:hidden;visibility:hidden"></div>
+
+        <div class="commands-subsection">
+          <div class="commands-header" data-reveal>
+            <p class="commands-header-subtitle">Pick any command to see it in action. <a href="/cheatsheet" class="cheatsheet-link">View cheatsheet &rarr;</a></p>
+          </div>
+          <div class="commands-gallery">
+            <!-- Rendered by JS -->
+          </div>
+        </div>
+      </div>
+    </section>
+
+    <!-- 03. THE ANTIDOTE - Anti-patterns -->
+    <section class="antidote-section" id="antidote">
+      <div class="section-header" data-reveal>
+        <span class="section-number">03</span>
+        <h2 class="section-title">The Antidote</h2>
+      </div>
+      <div class="antidote-content">
+        <p class="section-lead" data-reveal>Every AI model learned from the same templates. Without intervention, they all produce the same predictable mistakes. Impeccable names them, detects them, and teaches the AI to avoid them.</p>
+
+        <!-- Patterns + Gallery side by side -->
+        <div class="antidote-row" data-reveal>
+          <div class="patterns-tabbed" id="patterns-categories">
+            <!-- Rendered by JS -->
+          </div>
+
+          <div class="gallery-stack-container">
+            <div class="gallery-stack-header">
+              <h3 class="gallery-stack-title">Gallery of Shame</h3>
+              <button class="gallery-stack-btn gallery-stack-prev" aria-label="Previous">&larr;</button>
+              <button class="gallery-stack-btn gallery-stack-next" aria-label="Next">&rarr;</button>
+            </div>
+            <div class="gallery-stack" id="gallery-stack">
+              <a href="/antipattern-examples/purple-gradients.html" class="gallery-stack-card">
+                <img src="antipattern-images/purple-gradients.png" alt="Purple gradient UI" loading="lazy">
+                <span class="gallery-stack-label">Purple Gradients</span>
+              </a>
+              <a href="/antipattern-examples/thick-border-cards.html" class="gallery-stack-card">
+                <img src="antipattern-images/thick-border-cards.png" alt="Thick border accent cards" loading="lazy">
+                <span class="gallery-stack-label">Side-Tab Cards</span>
+              </a>
+              <a href="/antipattern-examples/cardocalypse.html" class="gallery-stack-card">
+                <img src="antipattern-images/cardocalypse.png" alt="Cards nested inside cards" loading="lazy">
+                <span class="gallery-stack-label">Cardocalypse</span>
+              </a>
+              <a href="/antipattern-examples/inter-everywhere.html" class="gallery-stack-card">
+                <img src="antipattern-images/inter-everywhere.png" alt="Inter font everywhere" loading="lazy">
+                <span class="gallery-stack-label">Inter Everywhere</span>
+              </a>
+              <a href="/antipattern-examples/layout-templates.html" class="gallery-stack-card">
+                <img src="antipattern-images/layout-templates.png" alt="Generic layout templates" loading="lazy">
+                <span class="gallery-stack-label">Template Layouts</span>
+              </a>
+              <a href="/antipattern-examples/bad-contrast.html" class="gallery-stack-card">
+                <img src="antipattern-images/bad-contrast.png" alt="Low contrast text" loading="lazy">
+                <span class="gallery-stack-label">Bad Contrast</span>
+              </a>
+            </div>
+            <a href="/visual-mode#try-it-live" class="gallery-stack-link">View full gallery &rarr;</a>
+          </div>
+        </div>
+
+        <div class="antidote-footer" data-reveal>
+          <a href="https://github.com/pbakaus/impeccable/issues/new?labels=pattern&title=Pattern%20suggestion%3A%20" class="antidote-suggest-link">Suggest a pattern &rarr;</a>
+        </div>
+      </div>
+    </section>
+
+    <!-- 04. VISUAL MODE - Browser overlay -->
+    <section class="visual-mode-section" id="visual-mode">
+      <div class="section-header" data-reveal>
+        <span class="section-number">04</span>
+        <h2 class="section-title">Visual Mode</h2>
+      </div>
+      <div class="visual-mode-content">
+        <p class="section-lead" data-reveal>See design issues highlighted directly on the page. No screenshots, no guesswork. Impeccable&rsquo;s overlay shows you exactly what&rsquo;s wrong and where.</p>
+
+        <div class="visual-mode-demo" data-reveal>
+          <div class="visual-mode-preview">
+            <div class="visual-mode-preview-header">
+              <span class="visual-mode-preview-dot red"></span>
+              <span class="visual-mode-preview-dot yellow"></span>
+              <span class="visual-mode-preview-dot green"></span>
+              <span class="visual-mode-preview-title">Live detection overlay</span>
+            </div>
+            <iframe src="/antipattern-examples/visual-mode-demo.html" class="visual-mode-iframe" loading="lazy" title="Anti-pattern detection overlay demo"></iframe>
+          </div>
+          <div class="visual-mode-details">
+            <div class="visual-mode-feature">
+              <span class="visual-mode-feature-label">25 deterministic checks</span>
+              <p>No LLM needed. Pattern matching catches purple gradients, overused fonts, nested cards, low contrast, and more.</p>
+            </div>
+            <div class="visual-mode-feature">
+              <span class="visual-mode-feature-label">Three ways to use it</span>
+              <p>The <strong>Chrome extension</strong> on any site, embedded in <code>/critique</code> during an AI design review, or standalone via <code>npx impeccable live</code>.</p>
+            </div>
+            <a href="https://chromewebstore.google.com/detail/impeccable/bdkgmiklpdmaojlpflclinlofgjfpabf" class="detection-callout" target="_blank" rel="noopener">
+              <img src="assets/extension-detection.png" alt="Impeccable Chrome extension panel listing detected anti-patterns" class="detection-callout-image" loading="lazy" width="600" height="508">
+              <div class="detection-callout-inner">
+                <span class="detection-eyebrow">Available now</span>
+                <span class="detection-callout-title">Chrome extension</span>
+                <span class="detection-cmd">Install from Chrome Web Store &rarr;</span>
+              </div>
+            </a>
+          </div>
+        </div>
+      </div>
+    </section>
+
+    <!-- 05. GET STARTED -->
+    <section class="platforms-section" id="downloads">
+      <div class="section-header" data-reveal>
+        <span class="section-number">05</span>
+        <h2 class="section-title">Get Started</h2>
+      </div>
+
+      <!-- Row 1: Primary install (full width, white card with internal 2-col split) -->
+      <div class="install-row-primary" data-reveal>
+        <div class="install-primary-main">
+          <h3 class="install-path-title"><span class="install-path-step">1</span>Install the skills <span class="install-path-badge">Recommended</span></h3>
+          <p class="install-path-desc">18 commands that steer your AI toward better design, in real time. The full Impeccable experience.</p>
+
+          <div class="install-path-terminal">
+            <div class="glass-terminal">
+              <div class="terminal-header">
+                <span class="terminal-dot red"></span>
+                <span class="terminal-dot yellow"></span>
+                <span class="terminal-dot green"></span>
+              </div>
+              <div class="terminal-body">
+                <div class="install-terminal-row">
+                  <div class="install-terminal-cmd">
+                    <span class="terminal-prompt">$</span>
+                    <code>npx skills add pbakaus/impeccable</code>
+                    <button class="copy-btn" aria-label="Copy command" data-copy="npx skills add pbakaus/impeccable">
+                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                        <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
+                        <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
+                      </svg>
+                    </button>
+                  </div>
+                  <span class="install-terminal-note">Works with Cursor, Claude Code, Gemini CLI, Codex CLI, and more.</span>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <div class="install-path-next">
+            Then run <code><span class="install-path-slash">/</span>impeccable teach</code> to set up your project's design context.
+          </div>
+        </div>
+
+        <!-- Other install methods -->
+        <div class="install-primary-alts">
+          <span class="install-alts-label">Other install methods</span>
+
+          <div class="install-alt-method">
+            <span class="install-alt-label">Claude Code plugin</span>
+            <div class="install-terminal-cmd">
+              <span class="terminal-prompt">$</span>
+              <code>/plugin marketplace add pbakaus/impeccable</code>
+              <button class="copy-btn" aria-label="Copy command" data-copy="/plugin marketplace add pbakaus/impeccable">
+                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                  <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
+                  <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
+                </svg>
+              </button>
+            </div>
+            <span class="install-alt-note">Then open <code>/plugin</code> in Claude Code</span>
+          </div>
+
+          <div class="install-alt-method">
+            <span class="install-alt-label">Manual download <span class="install-alt-sublabel">all 11 providers</span></span>
+            <div class="install-zip-links">
+              <button class="install-zip-link" data-bundle="universal" aria-label="Download universal zip with default command names">
+                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
+                  <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/>
+                </svg>
+                <span class="install-zip-link-name">Default zip</span>
+                <span class="install-zip-link-examples"><code>/polish</code> <code>/audit</code></span>
+              </button>
+              <button class="install-zip-link" data-bundle="universal-prefixed" aria-label="Download universal zip with prefixed command names">
+                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
+                  <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/>
+                </svg>
+                <span class="install-zip-link-name">Prefixed zip</span>
+                <span class="install-zip-link-examples"><code>/i-polish</code> <code>/i-audit</code></span>
+              </button>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- Steps 2-4: vertical accordion, mutually exclusive (only one open at a time) -->
+      <div class="install-accordion" data-reveal>
+        <!-- Step 2: CLI -->
+        <details name="install-steps" class="install-step">
+          <summary class="install-step-summary">
+            <h3 class="install-path-title"><span class="install-path-step">2</span>Add the CLI <span class="install-path-badge install-path-badge-muted">Beta</span></h3>
+            <svg class="install-step-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M9 6l6 6-6 6"/></svg>
+          </summary>
+          <div class="install-step-body">
+            <p class="install-path-desc">Scan any file, directory, or live URL for anti-patterns from the terminal. Catches gradient text, AI color palettes, nested cards, low contrast, and 20+ more rules across HTML, CSS, JSX/TSX, Vue, and Svelte. Use it in CI pipelines, pre-commit hooks, or one-off audits to keep AI slop out of production.</p>
+
+            <div class="install-cmd-block">
+              <div class="install-cmd-line">
+                <span class="terminal-prompt">$</span>
+                <code>npm i -g impeccable</code>
+                <button class="copy-btn" aria-label="Copy command" data-copy="npm i -g impeccable">
+                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                    <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
+                    <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
+                  </svg>
+                </button>
+              </div>
+              <span class="install-cmd-note">Or use <code>npx impeccable detect src/</code> directly without installing.</span>
+            </div>
+
+            <p class="install-path-link"><a href="https://www.npmjs.com/package/impeccable" target="_blank" rel="noopener">Full command reference on npm &rarr;</a></p>
+          </div>
+        </details>
+
+        <!-- Step 3: Browser extension -->
+        <details name="install-steps" class="install-step">
+          <summary class="install-step-summary">
+            <h3 class="install-path-title"><span class="install-path-step">3</span>Browser extension</h3>
+            <svg class="install-step-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M9 6l6 6-6 6"/></svg>
+          </summary>
+          <div class="install-step-body">
+            <p class="install-path-desc">Click the toolbar icon on any page and every anti-pattern lights up right where it lives: gradient text, purple palettes, nested cards, tiny body text, and the rest. Works on your localhost, staging, production, or anyone else's site. Great for spot-checking competitors, reviewing PRs visually, or just browsing the web with a sharper eye.</p>
+            <p class="install-path-link"><a href="https://chromewebstore.google.com/detail/impeccable/bdkgmiklpdmaojlpflclinlofgjfpabf" target="_blank" rel="noopener">Install from Chrome Web Store &rarr;</a></p>
+          </div>
+        </details>
+
+        <!-- Step 4: Stay updated -->
+        <details name="install-steps" class="install-step">
+          <summary class="install-step-summary">
+            <h3 class="install-path-title"><span class="install-path-step">4</span>Stay updated</h3>
+            <svg class="install-step-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M9 6l6 6-6 6"/></svg>
+          </summary>
+          <div class="install-step-body">
+            <p class="install-path-desc">Keep skills current and follow along with new commands, anti-patterns, and the design thinking behind Impeccable.</p>
+
+            <div class="install-cmd-block">
+              <div class="install-cmd-line">
+                <span class="terminal-prompt">$</span>
+                <code>npx impeccable skills update</code>
+                <button class="copy-btn" aria-label="Copy command" data-copy="npx impeccable skills update">
+                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                    <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
+                    <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
+                  </svg>
+                </button>
+              </div>
+              <span class="install-cmd-note">Run periodically to pull the latest skill definitions.</span>
+            </div>
+
+            <div class="install-updated-subscribe">
+              <iframe class="install-updated-substack" src="https://impeccablestyle.substack.com/embed" width="100%" height="130" frameborder="0" scrolling="no"></iframe>
+              <a href="https://x.com/impeccable_ai" class="install-updated-x" target="_blank" rel="noopener">
+                <svg class="install-updated-x-icon" width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
+                <span class="install-updated-x-label">Follow on X</span>
+                <span class="install-updated-x-handle">@impeccable_ai</span>
+              </a>
+            </div>
+          </div>
+        </details>
+      </div>
+    </section>
+
+    <!-- 5+6. CHANGELOG + FAQ (side-by-side on desktop) -->
+    <div class="changelog-faq-row">
+    <!-- 5. CHANGELOG -->
+    <section class="changelog-section" id="changelog">
+      <div class="section-header" data-reveal>
+        <span class="section-number">06</span>
+        <h2 class="section-title">What's New</h2>
+      </div>
+
+      <div class="changelog-list" data-reveal>
+        <div class="changelog-entry">
+          <div class="changelog-version-header">
+            <span class="changelog-version">v2.1</span>
+            <span class="changelog-date">April 9, 2026</span>
+          </div>
+          <ul class="changelog-items">
+            <li><strong>Streamlined from 21 to 18 commands.</strong> Removed overlap and confusion: <code>/arrange</code> renamed to <code>/layout</code>, <code>/normalize</code> merged into <code>/polish</code> (design system alignment is now part of the final pass), <code>/onboard</code> merged into <code>/harden</code> (empty states and first-run experiences are part of production readiness), and <code>/extract</code> became <code>/impeccable extract</code> (a sub-mode alongside craft and teach). Every remaining command has a clearly distinct job.</li>
+            <li><strong>Automatic cleanup of deprecated skills.</strong> On first load after updating, the skill detects and removes leftover files from renamed or merged commands. No manual cleanup needed.</li>
+          </ul>
+        </div>
+
+        <div class="changelog-entry">
+          <div class="changelog-version-header">
+            <span class="changelog-version">v2.0</span>
+            <span class="changelog-date">April 8, 2026</span>
+          </div>
+          <ul class="changelog-items">
+            <li><strong>Renamed <code>frontend-design</code> to <code>impeccable</code>.</strong> The core skill now shares its name with the project, and the teach subcommand moved from <code>/teach-impeccable</code> to <code>/impeccable teach</code>. One skill, one namespace.</li>
+            <li><strong>Data-driven skill rewrite.</strong> The core skill was rebuilt against an internal eval framework that runs the same brief through frontier models with and without the skill loaded, then measures how much the output collapses into monoculture. The result: dramatically more font and color diversity, sharper overall design quality, and much stronger Codex support. The biggest unlock was an anti-attractor procedure that forces the model to enumerate and reject its reflex defaults before picking. Validated on gpt-5.4 and Qwen 3.6 Plus across 15 niches.</li>
+            <li><strong>Anti-pattern detection engine.</strong> 25 deterministic rules across typography, color, layout, motion, and quality. Handles oklch, oklab, lch, and lab color formats, CSS variables inside border shorthands, gradient-backed text, and emoji-only nodes.</li>
+            <li><strong>CLI: <code>npx impeccable detect</code>.</strong> Scans HTML, CSS, JSX/TSX, Vue, Svelte, and CSS-in-JS. Framework detection, multi-file import tracking, Puppeteer-backed live URL scanning, CI-ready JSON output, and a <code>--fast</code> regex mode for huge codebases.</li>
+            <li><strong>Chrome DevTools extension.</strong> One-click detection on any page: yours, staging, production, or someone else's. Reads live computed styles, surfaces findings in an interactive panel, and highlights elements on the page. In Chrome Web Store review.</li>
+            <li><strong><code>/critique</code> got teeth.</strong> Persona sub-agents review in parallel, score against Nielsen's heuristics, run the detector automatically, and open a live browser overlay so you can walk each finding in place.</li>
+            <li><strong>New ways to create with Impeccable.</strong> <code>/shape</code> runs a structured discovery interview about purpose, audience, and goals, then produces a design brief before any code is written. <code>/impeccable craft</code> chains that brief straight into the full implementation flow so you ship a designed feature instead of a reflex card grid.</li>
+            <li><strong>New docs site.</strong> Top-level <a href="/skills">Docs</a>, <a href="/anti-patterns">Anti-Patterns</a>, and <a href="/visual-mode">Visual Mode</a> sections. 18 per-skill pages with before/after demos and the canonical SKILL.md inline, two <a href="/tutorials">tutorials</a>, and 38 rule cards with inline visual examples.</li>
+            <li><strong>New harness: Rovo Dev.</strong> 11 supported AI tools total.</li>
+          </ul>
+        </div>
+
+        <details class="changelog-older">
+          <summary class="changelog-older-toggle">View older releases</summary>
+          <div class="changelog-older-entries">
+            <div class="changelog-entry">
+              <div class="changelog-version-header">
+                <span class="changelog-version">v1.6.0</span>
+                <span class="changelog-date">March 18, 2026</span>
+              </div>
+              <ul class="changelog-items">
+                <li>New provider: <strong>Trae</strong> (China + International)</li>
+                <li><code>/critique</code> now scores against Nielsen's 10 heuristics, tests with persona archetypes, and assesses cognitive load</li>
+                <li><code>/audit</code> now scores 5 dimensions with P0-P3 severity ratings and structured action plans</li>
+                <li>Improved skill descriptions for better agent auto-discovery</li>
+                <li>Fixed invalid YAML frontmatter that broke GitHub preview and Codex loading (<a href="https://github.com/pbakaus/impeccable/issues/67">#67</a>)</li>
+                <li>Codex CLI now uses correct <code>$</code> prefix for command references</li>
+              </ul>
+            </div>
+
+            <div class="changelog-entry">
+              <div class="changelog-version-header">
+                <span class="changelog-version">v1.5.1</span>
+                <span class="changelog-date">March 17, 2026</span>
+              </div>
+              <ul class="changelog-items">
+                <li><code>/typeset</code> now recommends fixed type scales for app UIs, reserving fluid typography for marketing/content pages</li>
+              </ul>
+            </div>
+
+            <div class="changelog-entry">
+              <div class="changelog-version-header">
+                <span class="changelog-version">v1.5.0</span>
+                <span class="changelog-date">March 16, 2026</span>
+              </div>
+              <ul class="changelog-items">
+                <li>3 new skills: <code>/typeset</code> (fix typography), <code>/arrange</code> (fix layout & spacing), <code>/overdrive</code> (technically extraordinary effects, beta)</li>
+                <li>Skills now auto-gather design context via <code>.impeccable.md</code>. Run <code>/teach-impeccable</code> once, all skills benefit</li>
+                <li>Deep linking to commands (<code>#cmd-overdrive</code>, etc.)</li>
+              </ul>
+            </div>
+
+            <div class="changelog-entry">
+              <div class="changelog-version-header">
+                <span class="changelog-version">v1.3.0</span>
+                <span class="changelog-date">March 12, 2026</span>
+              </div>
+              <ul class="changelog-items">
+                <li>Added OpenCode provider support</li>
+                <li>Added Pi provider support</li>
+                <li>Recategorized <code>/onboard</code> as an enhancement command</li>
+              </ul>
+            </div>
+
+            <div class="changelog-entry">
+              <div class="changelog-version-header">
+                <span class="changelog-version">v1.2.0</span>
+                <span class="changelog-date">March 5, 2026</span>
+              </div>
+              <ul class="changelog-items">
+                <li>Added Kiro support (<code>.kiro/skills/</code>)</li>
+                <li>Restored prefix toggle: download <code>i-</code> prefixed bundles to avoid naming conflicts</li>
+                <li>Audit and critique skills only suggest real, installed commands</li>
+              </ul>
+            </div>
+
+            <div class="changelog-entry">
+              <div class="changelog-version-header">
+                <span class="changelog-version">v1.1.0</span>
+                <span class="changelog-date">March 4, 2026</span>
+              </div>
+              <ul class="changelog-items">
+                <li>Unified skills architecture: commands are now skills with <code>user-invocable: true</code></li>
+                <li>Added VS Code Copilot and Google Antigravity support (<code>.agents/skills/</code>)</li>
+                <li>New install flow: <code>npx skills add</code> as primary, universal ZIP as fallback</li>
+                <li>Added universal ZIP containing all 5 provider directories</li>
+                <li>Renamed <code>/simplify</code> to <code>/distill</code> to avoid Claude Code conflict</li>
+              </ul>
+            </div>
+
+            <div class="changelog-entry">
+              <div class="changelog-version-header">
+                <span class="changelog-version">v1.0.0</span>
+                <span class="changelog-date">February 28, 2026</span>
+              </div>
+              <ul class="changelog-items">
+                <li>Initial release with enhanced frontend-design skill</li>
+                <li>17 design commands: /polish, /audit, /distill, /bolder, and more</li>
+                <li>Support for Cursor, Claude Code, Gemini CLI, and Codex CLI</li>
+                <li>Interactive command cheatsheet</li>
+              </ul>
+            </div>
+          </div>
+        </details>
+      </div>
+
+    </section>
+
+    <!-- 6. FAQ -->
+    <section class="faq-section" id="faq">
+      <div class="section-header" data-reveal>
+        <span class="section-number">07</span>
+        <h2 class="section-title">Frequently Asked Questions</h2>
+      </div>
+
+      <div class="faq-list" data-reveal>
+        <details class="faq-item">
+          <summary class="faq-question">Where do I put the downloaded files?</summary>
+          <div class="faq-answer">
+            <p>The easiest way is <code>npx skills add pbakaus/impeccable</code>, which auto-detects your AI harness and places files correctly.</p>
+            <p>If you downloaded the <strong>universal ZIP</strong>, extract it to your <strong>project root</strong> (same level as your <code>package.json</code> or <code>src/</code> folder). It creates hidden folders for each supported tool: <code>.cursor/</code>, <code>.claude/</code>, <code>.gemini/</code>, <code>.codex/</code>, and <code>.agents/</code>.</p>
+            <p>Project-level installation takes precedence and lets you version control your skills.</p>
+          </div>
+        </details>
+
+        <details class="faq-item">
+          <summary class="faq-question">How do I update to the latest version?</summary>
+          <div class="faq-answer">
+            <p>Run <code>npx impeccable skills update</code> from your project root. It downloads the latest skills, cleans up deprecated files, and preserves any prefix you use.</p>
+            <ul>
+              <li><strong>Alternative:</strong> <code>npx skills add pbakaus/impeccable</code> re-installs from scratch.</li>
+              <li><strong>Claude Code plugin:</strong> Open <code>/plugin</code>, go to the Discover tab.</li>
+              <li><strong>Manual ZIP:</strong> Download from above and extract to the project root.</li>
+            </ul>
+            <p>Your <code>.impeccable.md</code> context file is never overwritten.</p>
+          </div>
+        </details>
+
+        <details class="faq-item">
+          <summary class="faq-question">Commands or skills aren't appearing. What do I do?</summary>
+          <div class="faq-answer">
+            <p><strong>For commands:</strong> Type <code>/</code> in your AI harness and look for commands like <code>/audit</code>, <code>/polish</code>, etc. If they don't appear, double-check the files are in the correct location.</p>
+            <p><strong>For skills:</strong> Skills are applied automatically when relevant. To verify, explicitly mention "use the impeccable skill" in your prompt. This forces the AI to acknowledge and apply it.</p>
+            <p><strong>Tool-specific setup:</strong></p>
+            <ul>
+              <li><strong>Cursor:</strong> Requires Nightly channel + Agent Skills enabled in Settings → Rules</li>
+              <li><strong>Gemini CLI:</strong> Requires <code>@google/gemini-cli@preview</code> + Skills enabled via <code>/settings</code></li>
+            </ul>
+          </div>
+        </details>
+
+        <details class="faq-item">
+          <summary class="faq-question">I'm new to AI harnesses. Where do I start?</summary>
+          <div class="faq-answer">
+            <p>Skills and commands are intermediate features. If you're just getting started, learn the basics first:</p>
+            <ul>
+              <li><strong>Claude Code:</strong> <a href="https://docs.anthropic.com/en/docs/claude-code" target="_blank" rel="noopener">Official Documentation</a></li>
+              <li><strong>Cursor:</strong> <a href="https://docs.cursor.com" target="_blank" rel="noopener">Cursor Docs</a></li>
+              <li><strong>Gemini CLI:</strong> <a href="https://geminicli.com/docs/" target="_blank" rel="noopener">Gemini CLI Docs</a></li>
+              <li><strong>Codex CLI:</strong> <a href="https://github.com/openai/codex" target="_blank" rel="noopener">Codex GitHub</a></li>
+            </ul>
+            <p>Once you're comfortable with basic prompting and code generation, come back and give Impeccable a try.</p>
+          </div>
+        </details>
+
+        <details class="faq-item">
+          <summary class="faq-question">Is Impeccable free?</summary>
+          <div class="faq-answer">
+            <p>Yes. Everything is <strong>Apache 2.0</strong>: skills, commands, CLI, and the detection engine. Fully open source, free for everyone.</p>
+          </div>
+        </details>
+      </div>
+    </section>
+    </div><!-- /.changelog-faq-row -->
+
+    <!-- PARTNERSHIPS -->
+    <section class="consulting-section" id="consulting">
+      <div class="consulting-content" data-reveal>
+        <div class="consulting-text">
+          <h2 class="consulting-title">Work with me</h2>
+          <p class="consulting-desc">Impeccable is built by <a href="https://renaissance-geek.ai" target="_blank" rel="noopener">Renaissance Geek</a>. I work with enterprise teams on large-scale rollouts, custom integrations, and training for designers and developers. If you're a frontier lab, design tool company, or enterprise looking to raise the bar on AI-generated design, let's talk.</p>
+        </div>
+        <div class="consulting-actions">
+          <a href="mailto:paul@renaissance-geek.ai" class="btn btn-primary">
+            <span>Get in touch</span>
+            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <path d="M5 12h14M12 5l7 7-7 7"/>
+            </svg>
+          </a>
+        </div>
+      </div>
+    </section>
+  </main>
+
+  <footer class="site-footer">
+    <div class="footer-row">
+      <span class="footer-logo">Impeccable</span>
+      <nav class="footer-links" aria-label="Footer">
+        <a href="#antidote">Anti-Patterns</a>
+        <a href="/visual-mode">Visual Mode</a>
+        <a href="#language">Commands</a>
+        <a href="/cheatsheet">Cheatsheet</a>
+        <a href="/privacy">Privacy</a>
+        <a href="https://github.com/pbakaus/impeccable">GitHub</a>
+      </nav>
+      <div class="footer-credit">
+        <span>Created by <a href="https://x.com/pbakaus" target="_blank" rel="noopener">Paul Bakaus</a></span>
+        <a href="https://x.com/pbakaus" class="footer-social-link" aria-label="Follow on X" target="_blank" rel="noopener">
+          <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
+            <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
+          </svg>
+        </a>
+        <a href="https://linkedin.com/in/paulbakaus" class="footer-social-link" aria-label="Connect on LinkedIn" target="_blank" rel="noopener">
+          <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
+            <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
+          </svg>
+        </a>
+      </div>
+    </div>
+  </footer>
+
+  <script type="module" src="./app.js"></script>
+</body>
+</html>

public/js/components/art-gallery.js 🔗

@@ -0,0 +1,142 @@
+import { readySkills, skillFocusAreas } from "../data.js";
+import { renderSkillDemo, setupDemoTabs } from "../demo-renderer.js";
+import { setupDemoToggles } from "../demo-toggles.js";
+
+export function initArtGallery() {
+	// Initial setup if needed
+}
+
+export function renderGallery(skills) {
+	const container = document.querySelector(".skills-gallery");
+	if (!container) return;
+
+	// Filter skills (hide impeccable as per original)
+	const filteredSkills = skills.filter((s) => s.id !== "impeccable");
+
+	container.innerHTML = `
+        <div class="gallery-track">
+            ${filteredSkills.map((skill, index) => renderFrame(skill, index)).join("")}
+        </div>
+        <div class="gallery-map" role="tablist" aria-label="Skill gallery navigation">
+            ${filteredSkills.map((skill, index) => `<button class="gallery-dot ${index === 0 ? "active" : ""}" data-index="${index}" role="tab" aria-selected="${index === 0 ? "true" : "false"}" aria-label="View ${formatName(skill.id)} skill"></button>`).join("")}
+        </div>
+    `;
+
+	setupInteractions();
+	setupDemoTabs();
+	setupDemoToggles();
+}
+
+function renderFrame(skill, index) {
+	const isReady = readySkills.includes(skill.id);
+	const focusAreas = skillFocusAreas[skill.id] || [];
+	const displayName = formatName(skill.id);
+
+	return `
+        <article class="gallery-frame ${index === 0 ? "active" : ""}" data-index="${index}" id="skill-${skill.id}">
+            <div class="gallery-content">
+                <div class="gallery-visual">
+                    ${isReady ? renderSkillDemo(skill.id) : renderComingSoonVisual(skill.id)}
+                </div>
+                <div class="gallery-info">
+                    <div class="gallery-header">
+                        <h3 class="gallery-title">${displayName}</h3>
+                        <div class="gallery-meta">
+                            Skill · ${isReady ? "Available" : "Coming Soon"}
+                        </div>
+                    </div>
+                    <p class="gallery-desc">${skill.description}</p>
+                    
+                    ${
+											focusAreas.length > 0
+												? `
+                        <div class="gallery-tags">
+                            ${focusAreas
+															.slice(0, 4)
+															.map(
+																(area) => `
+                                <span class="gallery-tag">${area.area}</span>
+                            `,
+															)
+															.join("")}
+                        </div>
+                    `
+												: ""
+										}
+                </div>
+            </div>
+        </article>
+    `;
+}
+
+function renderComingSoonVisual(id) {
+	return `
+        <div class="coming-soon-placeholder" style="text-align: center;">
+            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" style="opacity: 0.3">
+                <path d="M12 6v6l4 2M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/>
+            </svg>
+            <p style="margin-top: 1rem; color: var(--color-ash); font-size: 0.875rem;">Coming Soon</p>
+        </div>
+    `;
+}
+
+function formatName(id) {
+	return id
+		.split("-")
+		.map((word) =>
+			word === "ux" ? "UX" : word.charAt(0).toUpperCase() + word.slice(1),
+		)
+		.join(" ");
+}
+
+function setupInteractions() {
+	const track = document.querySelector(".gallery-track");
+	const frames = document.querySelectorAll(".gallery-frame");
+	const dots = document.querySelectorAll(".gallery-dot");
+
+	if (!track) return;
+
+	// Intersection Observer for Active State
+	const observer = new IntersectionObserver(
+		(entries) => {
+			entries.forEach((entry) => {
+				if (entry.isIntersecting) {
+					const index = entry.target.dataset.index;
+
+					// Update frames
+					frames.forEach((f) => f.classList.remove("active"));
+					entry.target.classList.add("active");
+
+					// Update dots
+					dots.forEach((d) => {
+						d.classList.remove("active");
+						d.setAttribute("aria-selected", "false");
+					});
+					if (dots[index]) {
+						dots[index].classList.add("active");
+						dots[index].setAttribute("aria-selected", "true");
+					}
+				}
+			});
+		},
+		{
+			root: track,
+			threshold: 0.6,
+		},
+	);
+
+	frames.forEach((frame) => observer.observe(frame));
+
+	// Dot navigation
+	dots.forEach((dot, index) => {
+		dot.addEventListener("click", () => {
+			const frame = frames[index];
+			if (frame) {
+				frame.scrollIntoView({ behavior: "smooth", inline: "center" });
+			}
+		});
+	});
+}
+
+
+

public/js/components/foundation-animations.js 🔗

@@ -0,0 +1,53 @@
+export const foundationAnimations = {
+	'Typography': `
+		<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="foundation-svg">
+			<path d="M10 30L20 10L30 30" stroke="var(--color-mist)" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
+			<path d="M10 30L20 10L30 30" stroke="var(--color-accent)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="anim-draw"/>
+			<path d="M15 22H25" stroke="var(--color-accent)" stroke-width="1.5" stroke-linecap="round" class="anim-draw-delay"/>
+		</svg>
+	`,
+	'Color & Contrast': `
+		<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="foundation-svg">
+			<circle cx="16" cy="20" r="8" stroke="var(--color-ink)" stroke-width="1.5" class="anim-move-x"/>
+			<circle cx="24" cy="20" r="8" stroke="var(--color-accent)" stroke-width="1.5" class="anim-move-x-opp"/>
+			<path d="M20 14.5C21.5 16 22.5 18 22.5 20C22.5 22 21.5 24 20 25.5C18.5 24 17.5 22 17.5 20C17.5 18 18.5 16 20 14.5Z" fill="var(--color-accent)" class="anim-fade-in"/>
+		</svg>
+	`,
+	'Spatial Design': `
+		<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="foundation-svg">
+			<rect x="5" y="10" width="30" height="18.5" stroke="var(--color-mist)" stroke-width="1"/>
+			<line x1="23.5" y1="10" x2="23.5" y2="28.5" stroke="var(--color-mist)" stroke-width="1"/>
+			<line x1="23.5" y1="21.5" x2="35" y2="21.5" stroke="var(--color-mist)" stroke-width="1"/>
+			<path d="M5 28.5C5 18.28 13.28 10 23.5 10C29.85 10 35 15.15 35 21.5C35 25.42 31.82 28.5 27.9 28.5" fill="none" stroke="var(--color-accent)" stroke-width="1.5" stroke-linecap="round" class="anim-draw"/>
+		</svg>
+	`,
+	'Responsive': `
+		<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="foundation-svg">
+			<rect x="6" y="8" width="28" height="24" rx="2" stroke="var(--color-ink)" stroke-width="1.5" class="anim-res-frame"/>
+			<rect x="9" y="12" width="10" height="8" rx="1" fill="var(--color-accent)" class="anim-res-img"/>
+			<rect x="22" y="12" width="10" height="2" rx="0.5" fill="var(--color-ink)" class="anim-res-title"/>
+			<rect x="22" y="16.5" width="10" height="1.5" rx="0.5" fill="var(--color-ash)" class="anim-res-line-1"/>
+			<rect x="22" y="20" width="8" height="1.5" rx="0.5" fill="var(--color-ash)" class="anim-res-line-2"/>
+		</svg>
+	`,
+	'Interaction': `
+		<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="foundation-svg">
+			<rect x="10" y="14" width="20" height="12" rx="6" stroke="var(--color-ink)" stroke-width="1.5"/>
+			<circle cx="16" cy="20" r="4" fill="var(--color-mist)" class="anim-toggle-move"/>
+		</svg>
+	`,
+	'Motion': `
+		<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="foundation-svg">
+			<line x1="5" y1="32" x2="35" y2="32" stroke="var(--color-ink)" stroke-width="1.5"/>
+			<circle cx="20" cy="15" r="5" fill="var(--color-accent)" class="anim-squash-ball"/>
+		</svg>
+	`,
+	'UX Writing': `
+		<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="foundation-svg">
+			<rect x="8" y="12" width="18" height="2" rx="1" fill="var(--color-ink)"/>
+			<rect x="8" y="18" width="22" height="2" rx="1" fill="var(--color-ash)"/>
+			<rect x="8" y="24" width="14" height="2" rx="1" fill="var(--color-accent)"/>
+			<line x1="24" y1="23" x2="24" y2="27" stroke="var(--color-accent)" stroke-width="1.5" class="anim-blink"/>
+		</svg>
+	`
+};

public/js/components/foundation-grid.js 🔗

@@ -0,0 +1,26 @@
+import { skillFocusAreas, dimensionGuidelineCounts } from '../data.js';
+import { foundationAnimations } from './foundation-animations.js';
+
+export function initFoundationGrid() {
+	const container = document.querySelector('.foundation-grid');
+	if (!container) return;
+
+	const dimensions = skillFocusAreas['impeccable'];
+	if (!dimensions) return;
+
+	container.innerHTML = dimensions.map((dim, i) => `
+		<div class="foundation-column">
+			<div class="foundation-card">
+				<div class="foundation-card-viz">
+					${foundationAnimations[dim.area] || ''}
+				</div>
+				<div class="foundation-card-header">
+					<span class="foundation-card-label">${dim.area}</span>
+					<span class="foundation-card-count">${dimensionGuidelineCounts[dim.area] || ''}</span>
+				</div>
+				<p class="foundation-card-detail">${dim.detail}</p>
+			</div>
+			<div class="foundation-plinth plinth-${i + 1}"></div>
+		</div>
+	`).join('');
+}

public/js/components/framework-viz.js 🔗

@@ -0,0 +1,366 @@
+/**
+ * Periodic Table of Commands
+ * Clean grid visualization showing all commands organized by category
+ * Hover tooltips show description and relationships inline.
+ */
+
+import { commandCategories, commandRelationships, betaCommands } from '../data.js';
+
+const categoryColors = {
+	create: { bg: 'var(--cat-create-bg)', border: 'var(--cat-create-border)', text: 'var(--cat-create-text)' },
+	evaluate: { bg: 'var(--cat-evaluate-bg)', border: 'var(--cat-evaluate-border)', text: 'var(--cat-evaluate-text)' },
+	refine: { bg: 'var(--cat-refine-bg)', border: 'var(--cat-refine-border)', text: 'var(--cat-refine-text)' },
+	simplify: { bg: 'var(--cat-simplify-bg)', border: 'var(--cat-simplify-border)', text: 'var(--cat-simplify-text)' },
+	harden: { bg: 'var(--cat-harden-bg)', border: 'var(--cat-harden-border)', text: 'var(--cat-harden-text)' },
+	system: { bg: 'var(--cat-system-bg)', border: 'var(--cat-system-border)', text: 'var(--cat-system-text)' }
+};
+
+const categoryLabels = {
+	create: 'Create',
+	evaluate: 'Evaluate',
+	refine: 'Refine',
+	simplify: 'Simplify',
+	harden: 'Harden',
+	system: 'System'
+};
+
+const commandSymbols = {
+	'shape': 'Sh',
+	'impeccable craft': 'Ic',
+	'impeccable': 'Im',
+	'overdrive': 'Od',
+	'critique': 'Cr',
+	'audit': 'Au',
+	'typeset': 'Ty',
+	'layout': 'La',
+	'colorize': 'Co',
+	'animate': 'An',
+	'delight': 'De',
+	'bolder': 'Bo',
+	'quieter': 'Qu',
+	'distill': 'Di',
+	'clarify': 'Cl',
+	'adapt': 'Ad',
+	'polish': 'Po',
+	'optimize': 'Op',
+	'harden': 'Ha',
+	'impeccable teach': 'It',
+	'impeccable extract': 'Ie'
+};
+
+const commandNumbers = {
+	'shape': 0,
+	'impeccable craft': 1, 'impeccable': 2, 'overdrive': 3,
+	'critique': 4, 'audit': 5,
+	'typeset': 6, 'layout': 7, 'colorize': 8, 'animate': 9,
+	'delight': 10, 'bolder': 11, 'quieter': 12,
+	'distill': 13, 'clarify': 14, 'adapt': 15,
+	'polish': 16, 'optimize': 17, 'harden': 18,
+	'impeccable teach': 19, 'impeccable extract': 20
+};
+
+// Map sub-commands to their display label and scroll target
+const commandDisplay = {
+	'impeccable craft': { label: '/impeccable craft', scrollTo: 'impeccable' },
+	'impeccable teach': { label: '/impeccable teach', scrollTo: 'impeccable' },
+	'impeccable extract': { label: '/impeccable extract', scrollTo: 'impeccable' },
+};
+
+export class PeriodicTable {
+	constructor(container) {
+		this.container = container;
+		this.activeTooltip = null;
+		this.activeElement = null;
+		this.init();
+	}
+
+	init() {
+		this.container.innerHTML = '';
+		this.container.style.cssText = `
+			display: flex;
+			flex-direction: column;
+			gap: 16px;
+			padding: 20px;
+			height: 100%;
+			box-sizing: border-box;
+			position: relative;
+		`;
+
+		this.renderTable();
+	}
+
+	renderTable() {
+		const groups = {};
+		Object.entries(commandCategories).forEach(([cmd, cat]) => {
+			if (!groups[cat]) groups[cat] = [];
+			groups[cat].push(cmd);
+		});
+
+		const categoryOrder = ['create', 'evaluate', 'refine', 'simplify', 'harden', 'system'];
+
+		const grid = document.createElement('div');
+		grid.style.cssText = `
+			display: grid;
+			grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+			gap: 16px;
+			flex: 1;
+		`;
+
+		categoryOrder.forEach(cat => {
+			const commands = groups[cat];
+			if (!commands) return;
+			const group = this.createCategoryGroup(cat, commands);
+			grid.appendChild(group);
+		});
+
+		this.container.appendChild(grid);
+	}
+
+	showTooltip(el, cmd) {
+		this.hideTooltip();
+
+		const rel = commandRelationships[cmd] || {};
+		const toArray = (val) => {
+			if (!val) return [];
+			if (Array.isArray(val)) return val;
+			return [val];
+		};
+
+		const pairs = toArray(rel.pairs);
+		const leadsTo = toArray(rel.leadsTo);
+		const combinesWith = toArray(rel.combinesWith);
+
+		// Build relationships line
+		let relParts = [];
+		if (pairs.length > 0) relParts.push(`pairs with ${pairs.map(p => '/' + p).join(', ')}`);
+		if (combinesWith.length > 0) relParts.push(`+ ${combinesWith.map(p => '/' + p).join(', ')}`);
+		if (leadsTo.length > 0) relParts.push(`then ${leadsTo.map(p => '/' + p).join(', ')}`);
+
+		// Strip category prefix from flow for cleaner display
+		const flow = (rel.flow || '').replace(/^[^:]+:\s*/, '');
+
+		const tooltip = document.createElement('div');
+		tooltip.className = 'ptable-tooltip';
+		tooltip.style.cssText = `
+			position: absolute;
+			z-index: 20;
+			background: var(--color-paper);
+			border: 1px solid var(--color-mist);
+			border-radius: 6px;
+			padding: 10px 14px;
+			box-shadow: 0 8px 24px -4px rgba(0,0,0,0.12);
+			pointer-events: none;
+			max-width: 280px;
+			opacity: 0;
+			transition: opacity 0.15s ease;
+		`;
+
+		tooltip.innerHTML = `
+			<div style="font-family: var(--font-body); font-size: 13px; color: var(--color-charcoal); line-height: 1.4; margin-bottom: ${relParts.length ? '6px' : '0'};">${flow}</div>
+			${relParts.length ? `<div style="font-family: var(--font-mono); font-size: 11px; color: var(--color-ash); line-height: 1.4;">${relParts.join(' · ')}</div>` : ''}
+		`;
+
+		this.container.appendChild(tooltip);
+
+		// Position relative to element
+		const elRect = el.getBoundingClientRect();
+		const containerRect = this.container.getBoundingClientRect();
+
+		const left = elRect.left - containerRect.left;
+		const top = elRect.bottom - containerRect.top + 6;
+
+		tooltip.style.left = `${Math.min(left, containerRect.width - 290)}px`;
+		tooltip.style.top = `${top}px`;
+
+		// Fade in
+		requestAnimationFrame(() => { tooltip.style.opacity = '1'; });
+
+		this.activeTooltip = tooltip;
+	}
+
+	hideTooltip() {
+		if (this.activeTooltip) {
+			this.activeTooltip.remove();
+			this.activeTooltip = null;
+		}
+	}
+
+	createCategoryGroup(category, commands) {
+		const colors = categoryColors[category];
+
+		const group = document.createElement('div');
+		group.style.cssText = `display: flex; flex-direction: column; gap: 6px;`;
+
+		const label = document.createElement('div');
+		label.style.cssText = `
+			font-family: var(--font-body);
+			font-size: 10px;
+			font-weight: 500;
+			text-transform: uppercase;
+			letter-spacing: 0.05em;
+			color: ${colors.text};
+			padding-left: 2px;
+		`;
+		label.textContent = categoryLabels[category];
+		group.appendChild(label);
+
+		const row = document.createElement('div');
+		row.style.cssText = `display: flex; flex-wrap: wrap; gap: 6px;`;
+
+		commands.forEach(cmd => {
+			const element = this.createElement(cmd, category);
+			row.appendChild(element);
+		});
+
+		group.appendChild(row);
+		return group;
+	}
+
+	createElement(cmd, category) {
+		const colors = categoryColors[category];
+		const display = commandDisplay[cmd];
+
+		const el = document.createElement('button');
+		el.type = 'button';
+		el.setAttribute('aria-label', `/${cmd} command - ${categoryLabels[category]}`);
+		el.style.cssText = `
+			width: 56px;
+			height: 64px;
+			background: ${colors.bg};
+			border: 1.5px solid ${colors.border};
+			border-radius: 5px;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			cursor: pointer;
+			transition: transform 0.15s ease, box-shadow 0.15s ease;
+			position: relative;
+			font-family: inherit;
+			padding: 0;
+		`;
+
+		// Atomic number
+		const number = document.createElement('div');
+		number.style.cssText = `
+			position: absolute;
+			top: 3px;
+			left: 5px;
+			font-family: var(--font-mono);
+			font-size: 7px;
+			color: ${colors.text};
+			opacity: 0.5;
+		`;
+		number.textContent = commandNumbers[cmd];
+		el.appendChild(number);
+
+		// Symbol
+		const symbol = document.createElement('div');
+		symbol.style.cssText = `
+			font-family: var(--font-display);
+			font-size: 20px;
+			font-weight: 500;
+			color: ${colors.text};
+			line-height: 1;
+		`;
+		symbol.textContent = commandSymbols[cmd];
+		el.appendChild(symbol);
+
+		// Command name
+		const name = document.createElement('div');
+		name.style.cssText = `
+			font-family: var(--font-mono);
+			font-size: ${display ? '6.5px' : '8px'};
+			color: ${colors.text};
+			opacity: 0.7;
+			margin-top: 3px;
+			text-align: center;
+			max-width: 52px;
+			line-height: 1.3;
+			${display ? '' : 'white-space: nowrap;'}
+		`;
+		name.textContent = display ? display.label : `/${cmd}`;
+		el.appendChild(name);
+
+		// Beta badge
+		if (betaCommands.includes(cmd)) {
+			const badge = document.createElement('div');
+			badge.style.cssText = `
+				position: absolute;
+				top: 2px;
+				right: 3px;
+				font-family: var(--font-mono);
+				font-size: 5px;
+				letter-spacing: 0.05em;
+				color: ${colors.text};
+				opacity: 0.45;
+				text-transform: uppercase;
+			`;
+			badge.textContent = 'β';
+			el.appendChild(badge);
+		}
+
+		// Hover/focus: show tooltip
+		const activate = () => {
+			el.style.transform = 'translateY(-2px)';
+			el.style.boxShadow = `0 4px 12px ${colors.border}40`;
+			this.showTooltip(el, cmd);
+
+			if (this.activeElement && this.activeElement !== el) {
+				this.activeElement.style.transform = 'translateY(0)';
+				this.activeElement.style.boxShadow = 'none';
+			}
+			this.activeElement = el;
+		};
+
+		const deactivate = () => {
+			el.style.transform = 'translateY(0)';
+			el.style.boxShadow = 'none';
+			this.hideTooltip();
+		};
+
+		el.addEventListener('mouseenter', activate);
+		el.addEventListener('mouseleave', deactivate);
+		el.addEventListener('focus', activate);
+		el.addEventListener('blur', deactivate);
+
+		el.addEventListener('touchstart', (e) => {
+			e.preventDefault();
+			activate();
+		}, { passive: false });
+
+		el.addEventListener('click', () => {
+			activate();
+			const scrollTarget = display ? display.scrollTo : cmd;
+
+			// Navigate the fisheye scroller to this command
+			const fisheyeList = document.getElementById('fisheye-list');
+			if (fisheyeList) {
+				const items = [...fisheyeList.querySelectorAll('.fisheye-item')];
+				const idx = items.findIndex(item => item.dataset.id === scrollTarget);
+				if (idx >= 0 && fisheyeList._scrollToCommand) {
+					// Scroll the commands section into view first
+					const section = document.querySelector('.commands-subsection');
+					if (section) section.scrollIntoView({ behavior: 'smooth', block: 'start' });
+					fisheyeList._scrollToCommand(idx);
+					return;
+				}
+			}
+
+			// Fallback: scroll to the spread element
+			const target = document.getElementById(`cmd-${scrollTarget}`);
+			if (target) {
+				target.scrollIntoView({ behavior: 'smooth', block: 'center' });
+			}
+		});
+
+		return el;
+	}
+}
+
+export function initFrameworkViz() {
+	const container = document.getElementById('framework-viz-container');
+	if (container) {
+		new PeriodicTable(container);
+	}
+}

public/js/components/glass-terminal.js 🔗

@@ -0,0 +1,643 @@
+import { renderCommandDemo, initCommandDemo } from "../demo-renderer.js";
+import { initSplitCompare } from "../effects/split-compare.js";
+import { commandProcessSteps, commandCategories, commandRelationships, betaCommands } from "../data.js";
+
+// Track current split instance and command for cleanup
+let currentSplitInstance = null;
+let currentCommandId = null;
+let sourceCache = {}; // Cache fetched source content
+
+const MOBILE_BREAKPOINT = 900;
+
+function isMobile() {
+    return window.innerWidth <= MOBILE_BREAKPOINT;
+}
+
+export function initGlassTerminal() {
+    // Initial setup if needed
+}
+
+export function renderTerminalLayout(commands) {
+    const container = document.querySelector('.commands-gallery');
+    if (!container) return;
+
+    if (isMobile()) {
+        renderMobileLayout(container, commands);
+    } else {
+        renderDesktopLayout(container, commands);
+    }
+
+    // Re-render on resize crossing breakpoint
+    let wasMobile = isMobile();
+    window.addEventListener('resize', () => {
+        const nowMobile = isMobile();
+        if (nowMobile !== wasMobile) {
+            wasMobile = nowMobile;
+            currentSplitInstance = null;
+            currentCommandId = null;
+            if (nowMobile) {
+                renderMobileLayout(container, commands);
+            } else {
+                renderDesktopLayout(container, commands);
+            }
+        }
+    });
+}
+
+// ============================================
+// DESKTOP LAYOUT - Magazine Spread
+// ============================================
+
+let magazineState = {
+    currentIndex: 0,
+    commands: [],
+    isTransitioning: false,
+    keyboardBound: false,
+    intersectionObserver: null
+};
+
+const categoryOrder = ['diagnostic', 'quality', 'intensity', 'adaptation', 'enhancement', 'system'];
+const categoryLabels = {
+    'create': 'Create',
+    'evaluate': 'Evaluate',
+    'refine': 'Refine',
+    'simplify': 'Simplify',
+    'harden': 'Harden',
+    'system': 'System'
+};
+
+function renderDesktopLayout(container, commands) {
+    magazineState.commands = commands;
+
+    let startIndex = -1;
+
+    // Filter out deprecated shims and sub-commands (no standalone demos)
+    const deprecated = new Set(['teach-impeccable', 'frontend-design', 'arrange', 'normalize', 'onboard', 'extract', 'impeccable craft', 'impeccable teach', 'impeccable extract']);
+    const filteredCommands = commands.filter(c => !deprecated.has(c.id));
+
+    const categoryOrder = ['create', 'evaluate', 'refine', 'simplify', 'harden', 'system'];
+    const categoryLabelsShort = {
+        'create': 'Create', 'evaluate': 'Evaluate', 'refine': 'Refine',
+        'simplify': 'Simplify', 'harden': 'Harden', 'system': 'System'
+    };
+    // Preferred order within each category (unlisted commands append at end)
+    const categoryCommandOrder = {
+        'create': ['impeccable', 'shape'],
+        'evaluate': ['critique', 'audit'],
+        'refine': ['typeset', 'arrange', 'colorize', 'animate', 'delight', 'bolder', 'quieter', 'onboard', 'overdrive'],
+        'simplify': ['distill', 'clarify', 'adapt'],
+        'harden': ['normalize', 'polish', 'optimize', 'harden'],
+        'system': ['extract']
+    };
+    const grouped = {};
+    filteredCommands.forEach(cmd => {
+        const cat = commandCategories[cmd.id] || 'other';
+        if (!grouped[cat]) grouped[cat] = [];
+        grouped[cat].push(cmd);
+    });
+    // Sort each group by preferred order
+    Object.entries(grouped).forEach(([cat, cmds]) => {
+        const order = categoryCommandOrder[cat] || [];
+        cmds.sort((a, b) => {
+            const ai = order.indexOf(a.id);
+            const bi = order.indexOf(b.id);
+            return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
+        });
+    });
+    const orderedCommands = [];
+    const headerIndices = [];
+    categoryOrder.forEach(cat => {
+        if (!grouped[cat]) return;
+        headerIndices.push({ index: orderedCommands.length, label: categoryLabelsShort[cat] || cat });
+        orderedCommands.push(...grouped[cat]);
+    });
+    // Use ordered list for everything
+    filteredCommands.length = 0;
+    filteredCommands.push(...orderedCommands);
+    magazineState.commands = filteredCommands;
+
+    // Determine starting index: URL hash takes priority, otherwise default to "clarify"
+    const hash = window.location.hash;
+    if (hash && hash.startsWith('#cmd-')) {
+        const idx = filteredCommands.findIndex(c => c.id === hash.slice(5));
+        if (idx >= 0) startIndex = idx;
+    }
+    if (startIndex < 0) {
+        startIndex = Math.max(0, filteredCommands.findIndex(c => c.id === 'clarify'));
+    }
+    magazineState.currentIndex = startIndex;
+
+    // Build spreads HTML (after ordering so indices match fisheye)
+    const spreadsHTML = filteredCommands.map((cmd, i) => renderSpread(cmd, i, i === startIndex)).join('');
+
+    const fisheyeHTML = filteredCommands.map((cmd, i) => {
+        const cat = commandCategories[cmd.id] || 'other';
+        const isBeta = betaCommands.includes(cmd.id);
+        return `<button class="fisheye-item${i === startIndex ? ' is-active' : ''}" data-index="${i}" data-id="${cmd.id}" data-cat="${cat}"><span class="fisheye-slash">/</span>${cmd.id}${isBeta ? '<span class="fisheye-beta">BETA</span>' : ''}</button>`;
+    }).join('');
+
+    container.innerHTML = `
+        <div class="magazine-container">
+            <div class="fisheye-list" id="fisheye-list">
+                <div class="fisheye-scroll">${fisheyeHTML}</div>
+            </div>
+            <div class="magazine-viewport">
+                ${spreadsHTML}
+            </div>
+        </div>
+    `;
+
+    // Init demo for active spread
+    initSpreadDemo(startIndex);
+
+    // Set up interactions
+    setupFisheyeList(filteredCommands, headerIndices);
+    setupMagazineKeyboard(filteredCommands);
+    setupMagazineIntersection(container);
+}
+
+function renderSpread(cmd, index, isActive) {
+    const cat = commandCategories[cmd.id] || 'other';
+    const isBeta = betaCommands.includes(cmd.id);
+    const relationship = commandRelationships[cmd.id];
+    // Build relationship flow
+    let flowHTML = '';
+    if (relationship) {
+        if (relationship.pairs) {
+            flowHTML = `
+                <div class="spread-flow">
+                    <span class="spread-flow-icon">&#8596;</span>
+                    <span class="spread-flow-label">pairs with</span>
+                    <span class="spread-flow-cmd">/${relationship.pairs}</span>
+                </div>`;
+        } else if (relationship.leadsTo && relationship.leadsTo.length > 0) {
+            flowHTML = `
+                <div class="spread-flow">
+                    <span class="spread-flow-icon">&#8594;</span>
+                    <span class="spread-flow-label">leads to</span>
+                    ${relationship.leadsTo.map(c => `<span class="spread-flow-cmd">/${c}</span>`).join(' ')}
+                </div>`;
+        } else if (relationship.combinesWith && relationship.combinesWith.length > 0) {
+            flowHTML = `
+                <div class="spread-flow">
+                    <span class="spread-flow-icon">+</span>
+                    <span class="spread-flow-label">combines with</span>
+                    ${relationship.combinesWith.map(c => `<span class="spread-flow-cmd">/${c}</span>`).join(' ')}
+                </div>`;
+        }
+        if (!flowHTML && relationship.flow) {
+            flowHTML = `
+                <div class="spread-flow">
+                    <span class="spread-flow-label">${relationship.flow}</span>
+                </div>`;
+        }
+    }
+
+    return `
+        <div class="magazine-spread${isActive ? ' active' : ''}" data-index="${index}" data-category="${cat}" data-id="${cmd.id}" id="cmd-${cmd.id}">
+            <div class="spread-identity">
+                <span class="spread-category-label">${categoryLabels[cat] || cat}</span>
+                <h3 class="spread-command-name"><span class="spread-slash">/</span>${cmd.id}${isBeta ? '<span class="beta-badge">BETA</span>' : ''}</h3>
+                <p class="spread-description">${cmd.description}</p>
+                ${flowHTML}
+            </div>
+            <div class="spread-demo-area" data-demo-index="${index}">
+                <!-- Demo rendered lazily -->
+            </div>
+        </div>
+    `;
+}
+
+function initSpreadDemo(index) {
+    const cmd = magazineState.commands[index];
+    if (!cmd) return;
+
+    const spread = document.querySelector(`.magazine-spread[data-index="${index}"]`);
+    if (!spread) return;
+
+    const demoArea = spread.querySelector('.spread-demo-area');
+    if (!demoArea) return;
+
+    // Cleanup previous split instance
+    if (currentSplitInstance) {
+        currentSplitInstance.destroy();
+        currentSplitInstance = null;
+    }
+
+    currentCommandId = cmd.id;
+
+    // Only render HTML once; re-init split compare every time
+    if (demoArea.dataset.loaded !== 'true') {
+        demoArea.innerHTML = renderCommandDemo(cmd.id);
+        demoArea.dataset.loaded = 'true';
+    }
+
+    const splitComparison = demoArea.querySelector('.demo-split-comparison');
+    if (splitComparison) {
+        currentSplitInstance = initSplitCompare(splitComparison, {
+            defaultPosition: 50
+        });
+    }
+    initCommandDemo(cmd.id, demoArea);
+}
+
+function goToSpread(newIndex, commands) {
+    if (newIndex < 0 || newIndex >= commands.length) return;
+    if (newIndex === magazineState.currentIndex) return;
+
+    const oldIndex = magazineState.currentIndex;
+    magazineState.currentIndex = newIndex;
+
+    const spreads = document.querySelectorAll('.magazine-spread');
+
+    // Destroy the old split instance before switching
+    if (currentSplitInstance) {
+        currentSplitInstance.destroy();
+        currentSplitInstance = null;
+    }
+
+    // Mark old as exiting
+    spreads[oldIndex]?.classList.remove('active');
+    spreads[oldIndex]?.classList.add('exiting');
+
+    // Mark new as active
+    spreads[newIndex]?.classList.add('active');
+    spreads[newIndex]?.classList.remove('exiting');
+
+    // No fisheye sync here -- fisheye drives goToSpread, not the other way around
+
+    // Update URL hash
+    const cmd = commands[newIndex];
+    if (cmd) {
+        history.replaceState(null, '', `#cmd-${cmd.id}`);
+    }
+
+    // Init demo for new spread (lazy)
+    initSpreadDemo(newIndex);
+
+    // Clean exiting class after transition
+    setTimeout(() => {
+        spreads[oldIndex]?.classList.remove('exiting');
+    }, 500);
+}
+
+function setupFisheyeList(commands, headerIndices = []) {
+    const list = document.getElementById('fisheye-list');
+    const scroll = list?.querySelector('.fisheye-scroll');
+    const items = list ? [...list.querySelectorAll('.fisheye-item')] : [];
+    if (!list || !scroll || !items.length) return;
+
+    // Fixed item height (matches CSS). All math is index-based.
+    // -- Fisheye with absolute positioning --
+    // Each item is placed absolutely. Their Y positions are computed by
+    // accumulating scaled heights, so small items cluster together
+    // and the center item gets full space. Scroll position maps linearly
+    // to a fractional "center index" which drives everything.
+
+    const BASE_H = 36; // height of the center (scale=1) item
+    const MIN_SCALE = 0.35;
+    const RADIUS = 5;
+    const count = items.length;
+    const listH = list.clientHeight;
+    const centerY = listH / 2;
+    let currentActive = -1;
+
+    // Total scroll range: one "step" per item
+    const STEP = 30; // px of scroll per item advance
+    const totalScroll = (count - 1) * STEP;
+
+    // Set scroll container height to accommodate the range + centering padding
+    const spacer = document.createElement('div');
+    spacer.style.height = `${totalScroll + listH}px`;
+    scroll.appendChild(spacer);
+    // Initial scroll to center first item
+    scroll.scrollTop = 0;
+
+    // Map scrollTop to fractional center index
+    const getCenterIndex = () => scroll.scrollTop / STEP;
+
+    // Compute eased scale for a given distance from center
+    const getScale = (dist) => {
+        const ratio = Math.max(0, 1 - dist / RADIUS);
+        const eased = ratio * ratio * (3 - 2 * ratio); // smoothstep
+        return MIN_SCALE + eased * (1 - MIN_SCALE);
+    };
+
+    // Layout: position all items based on current center
+    const layout = (center) => {
+        // First, compute the Y position for each item by accumulating
+        // scaled heights, centered around the center item
+        const heights = items.map((_, i) => {
+            const dist = Math.abs(i - center);
+            return BASE_H * getScale(dist);
+        });
+
+        // Find the Y offset so the fractional center position lands at centerY.
+        // Interpolate between the integer positions for smooth scrolling.
+        const floorIdx = Math.max(0, Math.min(count - 1, Math.floor(center)));
+        const frac = center - floorIdx;
+
+        let yAtFloor = 0;
+        for (let i = 0; i < floorIdx; i++) yAtFloor += heights[i];
+        yAtFloor += heights[floorIdx] / 2;
+
+        // If between two items, blend toward the next
+        let yAtCeil = yAtFloor;
+        if (floorIdx < count - 1) {
+            yAtCeil = yAtFloor + heights[floorIdx] / 2 + heights[floorIdx + 1] / 2;
+        }
+        const yAtCenter = yAtFloor + (yAtCeil - yAtFloor) * frac;
+        const offset = centerY - yAtCenter + scroll.scrollTop;
+
+        // Position each item
+        let y = offset;
+        items.forEach((item, i) => {
+            const h = heights[i];
+            const scale = getScale(Math.abs(i - center));
+            const opacity = 0.25 + (scale - MIN_SCALE) / (1 - MIN_SCALE) * 0.75;
+
+            item.style.top = `${y}px`;
+            item.style.transform = `scale(${scale})`;
+            item.style.opacity = opacity;
+            y += h;
+        });
+    };
+
+    const activate = (idx) => {
+        idx = Math.max(0, Math.min(count - 1, Math.round(idx)));
+        if (idx === currentActive) return;
+        currentActive = idx;
+        items.forEach((it, i) => it.classList.toggle('is-active', i === idx));
+        goToSpread(idx, commands);
+    };
+
+    const scrollToIndex = (idx, behavior = 'smooth') => {
+        idx = Math.max(0, Math.min(count - 1, idx));
+        scroll.scrollTo({ top: idx * STEP, behavior });
+    };
+
+    // Scroll handler
+    let raf = null;
+    scroll.addEventListener('scroll', () => {
+        if (raf) cancelAnimationFrame(raf);
+        raf = requestAnimationFrame(() => {
+            const center = getCenterIndex();
+            layout(center);
+            activate(Math.round(center));
+        });
+    }, { passive: true });
+
+
+    // Click to jump
+    items.forEach((item, i) => {
+        item.addEventListener('click', () => scrollToIndex(i));
+    });
+
+    // Expose for keyboard/external nav
+    list._scrollToCommand = (idx) => scrollToIndex(idx);
+
+    // Init
+    const startIdx = magazineState.currentIndex;
+    currentActive = -1;
+    scroll.scrollTop = startIdx * STEP;
+    layout(startIdx);
+    activate(startIdx);
+}
+
+function setupMagazineKeyboard(commands) {
+    if (magazineState.keyboardBound) return;
+    magazineState.keyboardBound = true;
+
+    document.addEventListener('keydown', (e) => {
+        // Only respond when magazine is visible (desktop)
+        if (isMobile()) return;
+        const magazineEl = document.querySelector('.magazine-container');
+        if (!magazineEl) return;
+
+        // Check if magazine is somewhat in the viewport
+        const rect = magazineEl.getBoundingClientRect();
+        const inView = rect.top < window.innerHeight && rect.bottom > 0;
+        if (!inView) return;
+
+        const fisheyeList = document.getElementById('fisheye-list');
+        if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
+            e.preventDefault();
+            fisheyeList?._scrollToCommand?.(magazineState.currentIndex + 1);
+        } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
+            e.preventDefault();
+            fisheyeList?._scrollToCommand?.(magazineState.currentIndex - 1);
+        }
+    });
+}
+
+function setupMagazineIntersection(container) {
+    // When the magazine section enters the viewport, ensure the active demo is rendered
+    if (magazineState.intersectionObserver) {
+        magazineState.intersectionObserver.disconnect();
+    }
+
+    magazineState.intersectionObserver = new IntersectionObserver((entries) => {
+        entries.forEach(entry => {
+            if (entry.isIntersecting) {
+                initSpreadDemo(magazineState.currentIndex);
+            }
+        });
+    }, { threshold: 0.1 });
+
+    const magazineEl = container.querySelector('.magazine-container');
+    if (magazineEl) {
+        magazineState.intersectionObserver.observe(magazineEl);
+    }
+}
+
+function truncateDescription(text, maxLen = 120) {
+    if (text.length <= maxLen) return text;
+    // Cut at last sentence boundary within limit, or last word boundary
+    const truncated = text.slice(0, maxLen);
+    const lastPeriod = truncated.lastIndexOf('.');
+    if (lastPeriod > maxLen * 0.5) return truncated.slice(0, lastPeriod + 1);
+    const lastSpace = truncated.lastIndexOf(' ');
+    return truncated.slice(0, lastSpace) + '...';
+}
+
+// ============================================
+// MOBILE LAYOUT - Carousel + Sticky Demo
+// ============================================
+
+function renderMobileLayout(container, commands) {
+    // Build carousel pills
+    const carouselHTML = commands.map((cmd, i) => `
+        <button class="mobile-cmd-pill${i === 0 ? ' active' : ''}" data-id="${cmd.id}">
+            /${cmd.id}
+        </button>
+    `).join('');
+
+    // Build command info cards (one per command, only active one shown)
+    const infoCardsHTML = commands.map((cmd, i) => {
+        const relationship = commandRelationships[cmd.id];
+        let relationshipHTML = '';
+
+        if (relationship) {
+            if (relationship.pairs) {
+                relationshipHTML = `<div class="mobile-cmd-rel">↔ pairs with <code>/${relationship.pairs}</code></div>`;
+            } else if (relationship.leadsTo && relationship.leadsTo.length > 0) {
+                relationshipHTML = `<div class="mobile-cmd-rel">→ leads to ${relationship.leadsTo.map(c => `<code>/${c}</code>`).join(', ')}</div>`;
+            }
+        }
+
+        return `
+            <div class="mobile-cmd-info${i === 0 ? ' active' : ''}" data-id="${cmd.id}">
+                <h3 class="mobile-cmd-name">/${cmd.id}</h3>
+                <p class="mobile-cmd-desc">${cmd.description}</p>
+                ${relationshipHTML}
+            </div>
+        `;
+    }).join('');
+
+    container.innerHTML = `
+        <div class="mobile-commands-layout">
+            <div class="mobile-carousel-wrapper">
+                <div class="mobile-carousel">
+                    ${carouselHTML}
+                </div>
+            </div>
+            <div class="mobile-demo-area" id="mobile-demo-content">
+                ${renderCommandDemo(commands[0]?.id || 'audit')}
+            </div>
+            <div class="mobile-info-area">
+                ${infoCardsHTML}
+            </div>
+        </div>
+    `;
+
+    setupMobileInteractions(commands);
+}
+
+function setupMobileInteractions(commands) {
+    const pills = document.querySelectorAll('.mobile-cmd-pill');
+    const demoArea = document.getElementById('mobile-demo-content');
+    const infoCards = document.querySelectorAll('.mobile-cmd-info');
+
+    // Initialize first demo's split compare
+    const initialSplit = demoArea.querySelector('.demo-split-comparison');
+    if (initialSplit) {
+        currentSplitInstance = initSplitCompare(initialSplit, {
+            defaultPosition: 50,
+            minPosition: 10,
+            maxPosition: 90
+        });
+    }
+    if (commands[0]) initCommandDemo(commands[0].id, demoArea);
+
+    // Pill click/tap handler
+    pills.forEach(pill => {
+        pill.addEventListener('click', () => {
+            const cmdId = pill.dataset.id;
+            const cmd = commands.find(c => c.id === cmdId);
+            if (!cmd || currentCommandId === cmdId) return;
+
+            currentCommandId = cmdId;
+
+            // Update active pill
+            pills.forEach(p => p.classList.remove('active'));
+            pill.classList.add('active');
+
+            // Scroll pill into view horizontally
+            pill.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });
+
+            // Update info card
+            infoCards.forEach(card => {
+                card.classList.toggle('active', card.dataset.id === cmdId);
+            });
+
+            // Cleanup previous split
+            if (currentSplitInstance) {
+                currentSplitInstance.destroy();
+                currentSplitInstance = null;
+            }
+
+            // Update demo
+            demoArea.innerHTML = renderCommandDemo(cmdId);
+
+            // Init new split compare
+            const splitComparison = demoArea.querySelector('.demo-split-comparison');
+            if (splitComparison) {
+                currentSplitInstance = initSplitCompare(splitComparison, {
+                    defaultPosition: 50
+                });
+            }
+            initCommandDemo(cmdId, demoArea);
+        });
+    });
+}
+
+// ============================================
+// STACKED WINDOWS - Tab Switching
+// ============================================
+
+function setupStackTabs() {
+    const tabs = document.querySelectorAll('.terminal-stack-tab');
+    const demoWindow = document.querySelector('.terminal-window--demo');
+    const sourceWindow = document.querySelector('.terminal-window--source');
+
+    tabs.forEach(tab => {
+        tab.addEventListener('click', () => {
+            const view = tab.dataset.view;
+
+            // Update tab states
+            tabs.forEach(t => t.classList.remove('active'));
+            tab.classList.add('active');
+
+            // Switch windows
+            if (view === 'source') {
+                demoWindow.classList.add('is-back');
+                sourceWindow.classList.add('is-front');
+            } else {
+                demoWindow.classList.remove('is-back');
+                sourceWindow.classList.remove('is-front');
+            }
+        });
+    });
+}
+
+async function fetchCommandSource(cmdId) {
+    // Check cache first
+    if (sourceCache[cmdId]) {
+        return sourceCache[cmdId];
+    }
+
+    try {
+        const response = await fetch(`/api/command-source/${cmdId}`);
+        if (!response.ok) throw new Error('Failed to fetch source');
+        const data = await response.json();
+        sourceCache[cmdId] = data.content;
+        return data.content;
+    } catch (error) {
+        console.error('Error fetching command source:', error);
+        return null;
+    }
+}
+
+function escapeHtml(text) {
+    const div = document.createElement('div');
+    div.textContent = text;
+    return div.innerHTML;
+}
+
+async function updateSourceContent(cmdId) {
+    const titleEl = document.getElementById('source-title');
+    const contentEl = document.getElementById('source-content');
+
+    if (!titleEl || !contentEl) return;
+
+    titleEl.textContent = `${cmdId}.md`;
+    contentEl.innerHTML = '<span class="source-loading">Loading...</span>';
+
+    const source = await fetchCommandSource(cmdId);
+    if (source) {
+        contentEl.textContent = source;
+    } else {
+        contentEl.innerHTML = '<span class="source-loading">Source not available</span>';
+    }
+}
+

public/js/components/lens.js 🔗

@@ -0,0 +1,13 @@
+import { initSplitCompare } from "../effects/split-compare.js";
+
+export function initLensEffect() {
+	const container = document.getElementById("lens-comparison");
+	if (!container) return;
+
+	initSplitCompare(container, {
+		defaultPosition: 50
+	});
+}
+
+
+

public/js/components/section-nav.js 🔗

@@ -0,0 +1,80 @@
+/**
+ * Sticky Section Nav
+ * Shows/hides based on scroll position and highlights current section.
+ */
+
+export function initSectionNav() {
+	const nav = document.getElementById('section-nav');
+	if (!nav) return;
+
+	const items = nav.querySelectorAll('.section-nav-item');
+	const sectionIds = Array.from(items).map(item => item.dataset.section);
+
+	// Show/hide nav based on scroll position
+	const hero = document.getElementById('hero');
+	const footer = document.querySelector('.site-footer');
+	if (!hero) return;
+
+	let ticking = false;
+
+	function updateNav() {
+		const scrollY = window.scrollY;
+		const heroBottom = hero.offsetTop + hero.offsetHeight - 100;
+		const footerTop = footer ? footer.offsetTop : Infinity;
+		const viewportBottom = scrollY + window.innerHeight;
+
+		// Show nav after hero, hide when footer is visible
+		if (scrollY > heroBottom && viewportBottom < footerTop + 60) {
+			nav.classList.add('is-visible');
+		} else {
+			nav.classList.remove('is-visible');
+		}
+
+		// Find current section
+		let currentSection = null;
+		const viewportMiddle = scrollY + window.innerHeight * 0.4;
+
+		for (let i = sectionIds.length - 1; i >= 0; i--) {
+			const section = document.getElementById(sectionIds[i]);
+			if (section && section.offsetTop <= viewportMiddle) {
+				currentSection = sectionIds[i];
+				break;
+			}
+		}
+
+		// If the current section shares its top row with siblings (e.g. side-by-side
+		// changelog + FAQ on desktop), treat all of them as active.
+		const activeSections = new Set();
+		if (currentSection) {
+			const currentEl = document.getElementById(currentSection);
+			const currentTop = currentEl?.offsetTop ?? 0;
+			sectionIds.forEach(id => {
+				const el = document.getElementById(id);
+				if (el && Math.abs(el.offsetTop - currentTop) < 4) {
+					activeSections.add(id);
+				}
+			});
+		}
+
+		// Update active state
+		items.forEach(item => {
+			if (activeSections.has(item.dataset.section)) {
+				item.classList.add('is-active');
+			} else {
+				item.classList.remove('is-active');
+			}
+		});
+
+		ticking = false;
+	}
+
+	window.addEventListener('scroll', () => {
+		if (!ticking) {
+			requestAnimationFrame(updateNav);
+			ticking = true;
+		}
+	}, { passive: true });
+
+	// Initial check
+	updateNav();
+}

public/js/data.js 🔗

@@ -0,0 +1,140 @@
+// ============================================
+// DATA: Skill focus areas, command processes, relationships
+// ============================================
+
+// Items that are fully complete and ready for public use
+// All others will show "Coming Soon"
+export const readySkills = [
+  'impeccable'  // Consolidated skill with all design domains
+];
+
+export const readyCommands = [
+  'layout'  // First command to be fully completed
+];
+
+// Commands marked as beta — shown with a badge in the UI
+export const betaCommands = [
+  'overdrive'
+];
+
+// Consolidated impeccable skill with reference domains
+export const skillFocusAreas = {
+  'impeccable': [
+    { area: 'Typography', detail: 'Scale, rhythm, hierarchy, expression' },
+    { area: 'Color & Contrast', detail: 'Accessibility, systems, theming' },
+    { area: 'Spatial Design', detail: 'Layout, spacing, composition' },
+    { area: 'Responsive', detail: 'Fluid layouts, touch targets' },
+    { area: 'Interaction', detail: 'States, feedback, affordances' },
+    { area: 'Motion', detail: 'Micro-interactions, transitions' },
+    { area: 'UX Writing', detail: 'Clarity, voice, error messages' }
+  ]
+};
+
+// Guideline counts per dimension (verified from reference files)
+export const dimensionGuidelineCounts = {
+  'Typography': 33,
+  'Color & Contrast': 29,
+  'Spatial Design': 27,
+  'Motion': 32,
+  'Interaction': 36,
+  'Responsive': 23,
+  'UX Writing': 32
+};
+
+// Reference domains within the impeccable skill
+export const skillReferenceDomains = [
+  'typography',
+  'color-and-contrast',
+  'spatial-design',
+  'responsive-design',
+  'interaction-design',
+  'motion-design',
+  'ux-writing'
+];
+
+export const commandProcessSteps = {
+  'shape': ['Interview', 'Synthesize', 'Brief', 'Confirm'],
+  'impeccable craft': ['Shape', 'Reference', 'Build', 'Iterate'],
+  'impeccable': ['Direct', 'Design', 'Build', 'Refine'],
+  'overdrive': ['Assess', 'Choose', 'Build', 'Polish'],
+  'critique': ['Evaluate', 'Critique', 'Prioritize', 'Suggest'],
+  'audit': ['Scan', 'Document', 'Prioritize', 'Recommend'],
+  'typeset': ['Assess', 'Select', 'Scale', 'Refine'],
+  'layout': ['Assess', 'Grid', 'Rhythm', 'Balance'],
+  'colorize': ['Analyze', 'Strategy', 'Apply', 'Balance'],
+  'animate': ['Identify', 'Design', 'Implement', 'Polish'],
+  'delight': ['Identify', 'Design', 'Implement'],
+  'bolder': ['Analyze', 'Amplify', 'Impact'],
+  'quieter': ['Analyze', 'Reduce', 'Refine'],
+  'distill': ['Audit', 'Remove', 'Clarify'],
+  'clarify': ['Read', 'Simplify', 'Improve', 'Test'],
+  'adapt': ['Analyze', 'Adjust', 'Optimize'],
+  'polish': ['Discover', 'Review', 'Refine', 'Verify'],
+  'optimize': ['Profile', 'Identify', 'Improve', 'Measure'],
+  'harden': ['Test', 'Handle', 'Onboard', 'Validate'],
+  'impeccable teach': ['Explore', 'Interview', 'Synthesize', 'Save'],
+  'impeccable extract': ['Identify', 'Abstract', 'Migrate', 'Document']
+};
+
+export const commandCategories = {
+  // CREATE - build something new
+  'shape': 'create',
+  'impeccable craft': 'create',
+  'impeccable': 'create',
+  // EVALUATE - review and assess
+  'critique': 'evaluate',
+  'audit': 'evaluate',
+  // REFINE - improve existing design
+  'typeset': 'refine',
+  'layout': 'refine',
+  'colorize': 'refine',
+  'animate': 'refine',
+  'delight': 'refine',
+  'bolder': 'refine',
+  'quieter': 'refine',
+  'overdrive': 'refine',
+  // SIMPLIFY - reduce and clarify
+  'distill': 'simplify',
+  'clarify': 'simplify',
+  'adapt': 'simplify',
+  // HARDEN - production-ready
+  'polish': 'harden',
+  'optimize': 'harden',
+  'harden': 'harden',
+  // SYSTEM - setup and tooling
+  'impeccable teach': 'system',
+  'impeccable extract': 'system'
+};
+
+// Skill relationships - now consolidated into impeccable skill
+// The impeccable skill contains all domains as reference files
+export const skillRelationships = {
+  'impeccable': {
+    description: 'Comprehensive design intelligence with progressive reference loading',
+    referenceDomains: ['typography', 'color-and-contrast', 'spatial-design', 'responsive-design', 'interaction-design', 'motion-design', 'ux-writing']
+  }
+};
+
+export const commandRelationships = {
+  'shape': { flow: 'Create: Plan UX and UI through structured discovery' },
+  'impeccable craft': { flow: 'Create: Full shape-then-build flow with visual iteration' },
+  'impeccable': { flow: 'Create: Freeform design with full design intelligence' },
+  'overdrive': { combinesWith: ['animate', 'delight'], flow: 'Refine: Technically extraordinary effects' },
+  'critique': { leadsTo: ['polish', 'distill', 'bolder', 'quieter', 'typeset', 'layout'], flow: 'Evaluate: UX and design review with scoring' },
+  'audit': { leadsTo: ['harden', 'optimize', 'adapt', 'clarify'], flow: 'Evaluate: Technical quality audit' },
+  'typeset': { combinesWith: ['bolder', 'polish'], flow: 'Refine: Fix typography and type hierarchy' },
+  'layout': { combinesWith: ['distill', 'adapt'], flow: 'Refine: Fix layout and spacing' },
+  'colorize': { combinesWith: ['bolder', 'delight'], flow: 'Refine: Add strategic color' },
+  'animate': { combinesWith: ['delight'], flow: 'Refine: Add purposeful motion' },
+  'delight': { combinesWith: ['bolder', 'animate'], flow: 'Refine: Add personality and joy' },
+  'bolder': { pairs: 'quieter', flow: 'Refine: Amplify timid designs' },
+  'quieter': { pairs: 'bolder', flow: 'Refine: Tone down aggressive designs' },
+  'distill': { combinesWith: ['quieter', 'polish'], flow: 'Simplify: Strip to essence' },
+  'clarify': { combinesWith: ['polish', 'adapt'], flow: 'Simplify: Improve UX copy' },
+  'adapt': { combinesWith: ['polish', 'clarify'], flow: 'Simplify: Adapt for different contexts' },
+  'polish': { flow: 'Harden: Final pass and design system alignment' },
+  'optimize': { flow: 'Harden: Performance improvements' },
+  'harden': { combinesWith: ['optimize'], flow: 'Harden: Edge cases, onboarding, and error handling' },
+  'impeccable teach': { flow: 'System: One-time project design context setup' },
+  'impeccable extract': { flow: 'System: Extract design system components and tokens' }
+};

public/js/demo-renderer.js 🔗

@@ -0,0 +1,212 @@
+// ============================================
+// DEMO RENDERER - Generic rendering for command and skill demos
+// ============================================
+
+import { getCommandDemo } from './demos/commands/index.js';
+import { getSkillDemo } from './demos/skills/index.js';
+
+/**
+ * Initialize a command demo's JS after its HTML has been inserted into the DOM.
+ * Call this after innerHTML is set and split compare is initialized.
+ */
+export function initCommandDemo(commandId, container) {
+  const demo = getCommandDemo(commandId);
+  if (demo && typeof demo.init === 'function') {
+    const demoArea = container.querySelector('.split-after .split-content') || container;
+    console.log('[initCommandDemo]', commandId, 'demoArea:', demoArea);
+    demo.init(demoArea);
+  }
+}
+
+/**
+ * Render a command demo with split-screen comparison
+ */
+export function renderCommandDemo(commandId) {
+  const demo = getCommandDemo(commandId);
+
+  if (!demo) {
+    // impeccable has multiple modes — show a usage guide
+    if (commandId === 'impeccable') {
+      return `
+        <div class="demo-container">
+          <div class="demo-viewport" style="padding: var(--spacing-lg); font-size: 13px; line-height: 1.6;">
+            <div style="display: flex; flex-direction: column; gap: 16px; color: var(--color-ash);">
+              <div style="font-size: 14px; color: var(--color-text); font-weight: 600;">Three ways to use /impeccable</div>
+              <div style="display: flex; flex-direction: column; gap: 14px;">
+                <div style="display: flex; flex-direction: column; gap: 4px;">
+                  <div style="display: flex; gap: 8px; align-items: baseline;">
+                    <code style="font-size: 12px; color: var(--spread-accent, var(--color-accent)); font-weight: 600; white-space: nowrap;">/impeccable</code>
+                    <span style="opacity: 0.4; font-size: 11px;">freeform</span>
+                  </div>
+                  <span style="padding-left: 0; opacity: 0.8;">Use on any task. Loads full design intelligence, anti-patterns, and reference knowledge into the current context.</span>
+                </div>
+                <div style="display: flex; flex-direction: column; gap: 4px;">
+                  <div style="display: flex; gap: 8px; align-items: baseline;">
+                    <code style="font-size: 12px; color: var(--spread-accent, var(--color-accent)); font-weight: 600; white-space: nowrap;">/impeccable teach</code>
+                    <span style="opacity: 0.4; font-size: 11px;">one-time setup</span>
+                  </div>
+                  <span style="padding-left: 0; opacity: 0.8;">Scans your codebase, interviews you about brand and audience, then saves a Design Context that all other commands use automatically.</span>
+                </div>
+                <div style="display: flex; flex-direction: column; gap: 4px;">
+                  <div style="display: flex; gap: 8px; align-items: baseline;">
+                    <code style="font-size: 12px; color: var(--spread-accent, var(--color-accent)); font-weight: 600; white-space: nowrap;">/impeccable craft</code>
+                    <span style="opacity: 0.4; font-size: 11px;">build a feature</span>
+                  </div>
+                  <span style="padding-left: 0; opacity: 0.8;">Runs /shape to plan UX first, loads the right references, then builds and iterates visually until the result delights.</span>
+                </div>
+              </div>
+              <div style="font-size: 12px; opacity: 0.5; margin-top: 2px; font-style: italic;">Start with <code style="font-size: 11px;">/impeccable teach</code> once per project. Then use the other modes as needed.</div>
+            </div>
+          </div>
+        </div>
+      `;
+    }
+    // shape is a planning skill — show the process
+    if (commandId === 'shape') {
+      return `
+        <div class="demo-container">
+          <div class="demo-viewport" style="padding: var(--spacing-lg); font-size: 13px; line-height: 1.6;">
+            <div style="display: flex; flex-direction: column; gap: 16px; color: var(--color-ash);">
+              <div style="font-size: 14px; color: var(--color-text); font-weight: 600;">Design before you build</div>
+              <div style="display: flex; flex-direction: column; gap: 14px;">
+                <div style="display: flex; flex-direction: column; gap: 4px;">
+                  <div style="display: flex; gap: 8px; align-items: baseline;">
+                    <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">1. Discovery</span>
+                  </div>
+                  <span style="opacity: 0.8;">Interviews you about purpose, audience, content, constraints, and anti-goals. Adapts questions based on your answers.</span>
+                </div>
+                <div style="display: flex; flex-direction: column; gap: 4px;">
+                  <div style="display: flex; gap: 8px; align-items: baseline;">
+                    <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">2. Design Brief</span>
+                  </div>
+                  <span style="opacity: 0.8;">Synthesizes a 9-section brief: feature summary, primary action, design direction, layout strategy, key states, interaction model, content needs, recommended references, and open questions.</span>
+                </div>
+                <div style="display: flex; flex-direction: column; gap: 4px;">
+                  <div style="display: flex; gap: 8px; align-items: baseline;">
+                    <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">3. Handoff</span>
+                  </div>
+                  <span style="opacity: 0.8;">The confirmed brief guides <code style="font-size: 11px;">/impeccable craft</code> or any other implementation approach. No code written, just the thinking that makes code good.</span>
+                </div>
+              </div>
+              <div style="font-size: 12px; opacity: 0.5; margin-top: 2px; font-style: italic;">Use standalone or as the first step of <code style="font-size: 11px;">/impeccable craft</code>.</div>
+            </div>
+          </div>
+        </div>
+      `;
+    }
+    return `
+      <div class="demo-container">
+        <div class="demo-viewport">
+          <div style="text-align: center; color: var(--color-ash); font-style: italic; padding: var(--spacing-lg);">
+            Visual demo for /${commandId} coming soon
+          </div>
+        </div>
+      </div>
+    `;
+  }
+
+  // Use split-screen comparison
+  return `
+    <div class="demo-split-comparison" data-demo="command-${demo.id}">
+      <div class="split-container">
+        <div class="split-before">
+          <div class="split-content">${demo.before}</div>
+        </div>
+        <div class="split-after">
+          <div class="split-content">${demo.after || demo.before}</div>
+        </div>
+        <div class="split-divider"></div>
+      </div>
+      <div class="demo-caption">${demo.caption}</div>
+    </div>
+  `;
+}
+
+/**
+ * Render a skill demo (with tabs if multiple demos)
+ */
+export function renderSkillDemo(skillId) {
+  const skill = getSkillDemo(skillId);
+
+  if (!skill || !skill.tabs || skill.tabs.length === 0) {
+    return `
+      <div class="demo-container">
+        <div class="demo-viewport">
+          <div style="text-align: center; color: var(--color-ash); padding: var(--spacing-xl);">
+            <p>Demo for ${skillId.replace(/-/g, ' ')} coming soon</p>
+          </div>
+        </div>
+      </div>
+    `;
+  }
+
+  const showTabs = skill.tabs.length > 1;
+
+  const tabs = showTabs ? skill.tabs.map((tab, i) => `
+    <button class="demo-tab ${i === 0 ? 'active' : ''}" data-demo-tab="${tab.id}" data-skill="${skillId}">
+      ${tab.label}
+    </button>
+  `).join('') : '';
+
+  const panels = skill.tabs.map((tab, i) => `
+    <div class="demo-panel ${i === 0 ? 'active' : ''}" data-demo-panel="${tab.id}">
+      ${renderSkillTabDemo(skillId, tab)}
+    </div>
+  `).join('');
+
+  return `
+    <div class="demo-tabbed-container">
+      ${showTabs ? `<div class="demo-tabs">${tabs}</div>` : ''}
+      <div class="demo-panels">
+        ${panels}
+      </div>
+    </div>
+  `;
+}
+
+/**
+ * Render a single skill tab demo
+ */
+function renderSkillTabDemo(skillId, tab) {
+  const hasToggle = tab.hasToggle !== false;
+  const demoId = `${skillId}-${tab.id}`;
+
+  return `
+    <div class="demo-container">
+      <div class="demo-header">
+        ${hasToggle ? `
+          <div class="demo-toggle">
+            <span class="demo-toggle-label active" id="${demoId}-before-label">Before</span>
+            <button class="demo-toggle-switch" data-demo="${demoId}" role="switch" aria-checked="false" aria-labelledby="${demoId}-before-label ${demoId}-after-label"></button>
+            <span class="demo-toggle-label" id="${demoId}-after-label">After</span>
+          </div>
+        ` : ''}
+      </div>
+      <div class="demo-viewport" data-state="before" id="${demoId}-viewport">
+        ${tab.before}
+      </div>
+      <div class="demo-caption">${tab.caption}</div>
+    </div>
+  `;
+}
+
+/**
+ * Setup demo tab switching
+ */
+export function setupDemoTabs() {
+  document.querySelectorAll('.demo-tab').forEach(tab => {
+    tab.addEventListener('click', () => {
+      const tabId = tab.dataset.demoTab;
+      const container = tab.closest('.demo-tabbed-container');
+
+      container.querySelectorAll('.demo-tab').forEach(t => t.classList.remove('active'));
+      tab.classList.add('active');
+
+      container.querySelectorAll('.demo-panel').forEach(p => p.classList.remove('active'));
+      container.querySelector(`[data-demo-panel="${tabId}"]`)?.classList.add('active');
+    });
+  });
+}
+
+
+

public/js/demo-toggles.js 🔗

@@ -0,0 +1,160 @@
+// ============================================
+// DEMO TOGGLES - Handle before/after toggle interactions
+// ============================================
+
+import { getCommandDemo } from './demos/commands/index.js';
+import { getSkillDemo } from './demos/skills/index.js';
+
+/**
+ * Setup toggle handlers for skill demos
+ */
+export function setupDemoToggles() {
+  document.querySelectorAll('.demo-toggle-switch').forEach(toggle => {
+    // Skip if already has handler
+    if (toggle.dataset.initialized) return;
+    toggle.dataset.initialized = 'true';
+    
+    toggle.addEventListener('click', () => {
+      const demoId = toggle.dataset.demo;
+      const isActive = toggle.classList.toggle('active');
+
+      // Update ARIA state
+      toggle.setAttribute('aria-checked', isActive ? 'true' : 'false');
+
+      // Update labels
+      const labels = toggle.parentElement.querySelectorAll('.demo-toggle-label');
+      labels[0].classList.toggle('active', !isActive);
+      labels[1].classList.toggle('active', isActive);
+
+      // Update demo state
+      handleDemoToggle(demoId, isActive);
+    });
+  });
+  
+  // Setup interactive buttons
+  setupInteractiveButtons();
+}
+
+/**
+ * Setup toggle handlers for command demos
+ */
+export function setupCommandDemoToggles(allCommands, selectCommand) {
+  document.querySelectorAll('.command-demo-area .demo-toggle-switch').forEach(toggle => {
+    // Skip if already has handler
+    if (toggle.dataset.initialized) return;
+    toggle.dataset.initialized = 'true';
+    
+    toggle.addEventListener('click', () => {
+      const demoId = toggle.dataset.demo;
+      const isActive = toggle.classList.toggle('active');
+
+      // Update ARIA state
+      toggle.setAttribute('aria-checked', isActive ? 'true' : 'false');
+
+      const labels = toggle.parentElement.querySelectorAll('.demo-toggle-label');
+      labels[0].classList.toggle('active', !isActive);
+      labels[1].classList.toggle('active', isActive);
+
+      handleCommandDemoToggle(demoId, isActive);
+    });
+  });
+  
+  document.querySelectorAll('.command-detail-panel .relationship-tag').forEach(tag => {
+    tag.addEventListener('click', () => {
+      const commandId = tag.dataset.command;
+      const command = allCommands.find(c => c.id === commandId);
+      if (command) selectCommand(command);
+    });
+  });
+}
+
+/**
+ * Setup interactive demo buttons (like the "like" button)
+ */
+function setupInteractiveButtons() {
+  document.querySelectorAll('.int-fb-active[data-action="like"]').forEach(btn => {
+    if (btn.dataset.initialized) return;
+    btn.dataset.initialized = 'true';
+    
+    btn.addEventListener('click', () => {
+      btn.classList.toggle('liked');
+      const label = btn.nextElementSibling;
+      if (label) {
+        label.textContent = btn.classList.contains('liked') ? 'Liked!' : 'Click to try!';
+      }
+    });
+  });
+}
+
+/**
+ * Handle skill demo toggle
+ */
+function handleDemoToggle(demoId, isAfter) {
+  const viewport = document.getElementById(`${demoId}-viewport`);
+  if (!viewport) return;
+  
+  viewport.dataset.state = isAfter ? 'after' : 'before';
+  
+  // Parse skill ID and tab ID from demoId (e.g., "ux-writing-errors")
+  const parts = demoId.split('-');
+  const tabId = parts.pop();
+  const skillId = parts.join('-');
+  
+  const skill = getSkillDemo(skillId);
+  if (!skill) return;
+  
+  const tab = skill.tabs.find(t => t.id === tabId);
+  if (!tab) return;
+  
+  // Check for custom toggle handler
+  if (tab.onToggle) {
+    tab.onToggle(viewport, isAfter);
+    return;
+  }
+  
+  // Check for CSS class toggle
+  if (tab.beforeClass && tab.afterClass) {
+    const demo = viewport.firstElementChild;
+    if (demo) {
+      demo.className = isAfter ? tab.afterClass : tab.beforeClass;
+    }
+    return;
+  }
+  
+  // HTML swap
+  if (tab.after && tab.before) {
+    viewport.innerHTML = isAfter ? tab.after : tab.before;
+    
+    // Run after-render callback if exists
+    if (isAfter && tab.onAfterRender) {
+      tab.onAfterRender();
+    }
+  }
+}
+
+/**
+ * Handle command demo toggle
+ */
+function handleCommandDemoToggle(demoId, isAfter) {
+  // Extract command ID from demoId (e.g., "command-normalize" -> "normalize")
+  const commandId = demoId.replace('command-', '');
+  const demo = getCommandDemo(commandId);
+  
+  if (!demo) return;
+  
+  const viewport = document.getElementById(`${demoId}-viewport`);
+  if (!viewport) return;
+  
+  viewport.dataset.state = isAfter ? 'after' : 'before';
+  
+  // Check for custom toggle handler
+  if (demo.onToggle) {
+    demo.onToggle(viewport, isAfter);
+    return;
+  }
+  
+  // HTML swap
+  if (demo.after && demo.before) {
+    viewport.innerHTML = isAfter ? demo.after : demo.before;
+  }
+}

public/js/demos/commands/adapt.js 🔗

@@ -0,0 +1,80 @@
+// Adapt command demo - shows desktop-only design becoming responsive
+export default {
+  id: 'adapt',
+  caption: 'Fixed layout → Responsive across devices',
+
+  before: `
+    <div style="width: 100%; max-width: 280px; display: flex; gap: 12px; align-items: flex-end;">
+      <div style="text-align: center;">
+        <div style="width: 48px; height: 80px; background: #f5f5f5; border: 2px solid #ddd; border-radius: 6px; padding: 4px; box-sizing: border-box;">
+          <div style="height: 100%; display: flex; flex-direction: column; gap: 2px; overflow: hidden;">
+            <div style="height: 16px; background: #ccc; border-radius: 2px;"></div>
+            <div style="height: 8px; background: #e0e0e0; border-radius: 1px; width: 80%;"></div>
+            <div style="height: 8px; background: #e0e0e0; border-radius: 1px; font-size: 6px; color: #999; overflow: hidden;">Text too small...</div>
+          </div>
+        </div>
+        <div style="font-size: 9px; color: #999; margin-top: 4px;">Mobile</div>
+        <div style="font-size: 8px; color: #cc0000;">Broken ✗</div>
+      </div>
+      <div style="text-align: center;">
+        <div style="width: 100px; height: 70px; background: #f5f5f5; border: 2px solid #ddd; border-radius: 4px; padding: 6px; box-sizing: border-box;">
+          <div style="display: flex; gap: 4px; height: 100%;">
+            <div style="width: 25%; background: #ccc; border-radius: 2px;"></div>
+            <div style="flex: 1; display: flex; flex-direction: column; gap: 2px;">
+              <div style="height: 12px; background: #e0e0e0; border-radius: 2px;"></div>
+              <div style="flex: 1; background: #eee; border-radius: 2px;"></div>
+            </div>
+          </div>
+        </div>
+        <div style="font-size: 9px; color: #999; margin-top: 4px;">Desktop</div>
+        <div style="font-size: 8px; color: #22c55e;">Works ✓</div>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 280px; display: flex; gap: 12px; align-items: flex-end;">
+      <div style="text-align: center;">
+        <div style="width: 48px; height: 80px; background: var(--color-paper); border: 2px solid var(--color-mist); border-radius: 6px; padding: 4px; box-sizing: border-box;">
+          <div style="height: 100%; display: flex; flex-direction: column; gap: 3px;">
+            <div style="height: 12px; background: var(--color-ink); border-radius: 2px;"></div>
+            <div style="flex: 1; background: var(--color-mist); border-radius: 2px;"></div>
+            <div style="height: 14px; background: var(--color-accent); border-radius: 2px;"></div>
+          </div>
+        </div>
+        <div style="font-size: 9px; color: var(--color-ash); margin-top: 4px;">Mobile</div>
+        <div style="font-size: 8px; color: #22c55e;">Stacked ✓</div>
+      </div>
+      <div style="text-align: center;">
+        <div style="width: 72px; height: 56px; background: var(--color-paper); border: 2px solid var(--color-mist); border-radius: 4px; padding: 4px; box-sizing: border-box;">
+          <div style="height: 100%; display: flex; flex-direction: column; gap: 2px;">
+            <div style="height: 10px; background: var(--color-ink); border-radius: 2px;"></div>
+            <div style="flex: 1; display: flex; gap: 2px;">
+              <div style="flex: 1; background: var(--color-mist); border-radius: 2px;"></div>
+              <div style="flex: 1; background: var(--color-mist); border-radius: 2px;"></div>
+            </div>
+          </div>
+        </div>
+        <div style="font-size: 9px; color: var(--color-ash); margin-top: 4px;">Tablet</div>
+        <div style="font-size: 8px; color: #22c55e;">2-col ✓</div>
+      </div>
+      <div style="text-align: center;">
+        <div style="width: 100px; height: 64px; background: var(--color-paper); border: 2px solid var(--color-mist); border-radius: 4px; padding: 4px; box-sizing: border-box;">
+          <div style="display: flex; gap: 3px; height: 100%;">
+            <div style="width: 20%; background: var(--color-charcoal); border-radius: 2px;"></div>
+            <div style="flex: 1; display: flex; flex-direction: column; gap: 2px;">
+              <div style="height: 10px; background: var(--color-ink); border-radius: 2px;"></div>
+              <div style="flex: 1; display: flex; gap: 2px;">
+                <div style="flex: 1; background: var(--color-mist); border-radius: 2px;"></div>
+                <div style="flex: 1; background: var(--color-mist); border-radius: 2px;"></div>
+                <div style="flex: 1; background: var(--color-mist); border-radius: 2px;"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div style="font-size: 9px; color: var(--color-ash); margin-top: 4px;">Desktop</div>
+        <div style="font-size: 8px; color: #22c55e;">Sidebar ✓</div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/animate.js 🔗

@@ -0,0 +1,44 @@
+// Animate command demo - shows static elements becoming choreographed
+export default {
+  id: 'animate',
+  caption: 'Static layout → Choreographed entrance',
+
+  before: `
+    <div style="width: 100%; max-width: 220px; display: flex; flex-direction: column; gap: 8px;">
+      <div style="height: 32px; background: #e0e0e0; border-radius: 4px;"></div>
+      <div style="height: 12px; background: #e0e0e0; border-radius: 2px; width: 60%;"></div>
+      <div style="display: flex; gap: 8px; margin-top: 8px;">
+        <div style="flex: 1; height: 64px; background: #e0e0e0; border-radius: 4px;"></div>
+        <div style="flex: 1; height: 64px; background: #e0e0e0; border-radius: 4px;"></div>
+      </div>
+      <div style="height: 10px; background: #e0e0e0; border-radius: 2px; width: 80%; margin-top: 8px;"></div>
+      <div style="height: 10px; background: #e0e0e0; border-radius: 2px; width: 65%;"></div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 220px; display: flex; flex-direction: column; gap: 8px;">
+      <div style="height: 32px; background: var(--color-ink); border-radius: 4px; animation: animDemoFade 0.5s ease-out both;"></div>
+      <div style="height: 12px; background: var(--color-ash); border-radius: 2px; width: 60%; animation: animDemoFade 0.5s ease-out 0.1s both;"></div>
+      <div style="display: flex; gap: 8px; margin-top: 8px;">
+        <div style="flex: 1; height: 64px; background: var(--color-mist); border-radius: 4px; animation: animDemoSlide 0.4s ease-out 0.2s both;"></div>
+        <div style="flex: 1; height: 64px; background: var(--color-mist); border-radius: 4px; animation: animDemoSlide 0.4s ease-out 0.3s both;"></div>
+      </div>
+      <div style="height: 10px; background: var(--color-mist); border-radius: 2px; width: 80%; margin-top: 8px; animation: animDemoFade 0.4s ease-out 0.4s both;"></div>
+      <div style="height: 10px; background: var(--color-mist); border-radius: 2px; width: 65%; animation: animDemoFade 0.4s ease-out 0.5s both;"></div>
+    </div>
+    <style>
+      @keyframes animDemoFade {
+        from { opacity: 0; transform: translateY(8px); }
+        to { opacity: 1; transform: translateY(0); }
+      }
+      @keyframes animDemoSlide {
+        from { opacity: 0; transform: translateY(16px) scale(0.95); }
+        to { opacity: 1; transform: translateY(0) scale(1); }
+      }
+    </style>
+  `
+};
+
+
+

public/js/demos/commands/arrange.js 🔗

@@ -0,0 +1,47 @@
+// Arrange command demo - shows monotonous equal spacing becoming rhythmic and intentional
+export default {
+  id: 'arrange',
+  caption: 'Equal spacing everywhere → Intentional rhythm and hierarchy',
+
+  before: `
+    <div style="width: 100%; max-width: 240px; padding: 16px;">
+      <div style="text-align: center; margin-bottom: 16px;">
+        <div style="font-size: 14px; font-weight: bold; color: #333;">Team Members</div>
+      </div>
+      <div style="display: flex; flex-direction: column; gap: 16px;">
+        <div style="padding: 16px; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 8px; text-align: center;">
+          <div style="width: 32px; height: 32px; background: #ddd; border-radius: 50%; margin: 0 auto 8px;"></div>
+          <div style="font-size: 13px; color: #333;">Alice Chen</div>
+          <div style="font-size: 12px; color: #888;">Designer</div>
+        </div>
+        <div style="padding: 16px; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 8px; text-align: center;">
+          <div style="width: 32px; height: 32px; background: #ddd; border-radius: 50%; margin: 0 auto 8px;"></div>
+          <div style="font-size: 13px; color: #333;">Bob Park</div>
+          <div style="font-size: 12px; color: #888;">Engineer</div>
+        </div>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 240px; padding: 16px; font-family: 'Instrument Sans', sans-serif;">
+      <div style="font-size: 0.8125rem; font-weight: 600; color: var(--color-ink); margin-bottom: 16px;">Team Members</div>
+      <div style="display: flex; flex-direction: column; gap: 6px;">
+        <div style="display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--color-mist);">
+          <div style="width: 28px; height: 28px; background: var(--color-accent); border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: white; font-size: 11px; font-weight: 600;">AC</div>
+          <div style="flex: 1;">
+            <div style="font-size: 0.8125rem; font-weight: 500; color: var(--color-ink);">Alice Chen</div>
+            <div style="font-size: 0.6875rem; color: var(--color-ash);">Designer</div>
+          </div>
+        </div>
+        <div style="display: flex; align-items: center; gap: 12px; padding: 10px 0;">
+          <div style="width: 28px; height: 28px; background: color-mix(in oklch, var(--color-accent) 60%, var(--color-ink)); border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: white; font-size: 11px; font-weight: 600;">BP</div>
+          <div style="flex: 1;">
+            <div style="font-size: 0.8125rem; font-weight: 500; color: var(--color-ink);">Bob Park</div>
+            <div style="font-size: 0.6875rem; color: var(--color-ash);">Engineer</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/audit.js 🔗

@@ -0,0 +1,40 @@
+// Audit command demo - shows scattered issues being identified and flagged
+export default {
+  id: 'audit',
+  caption: 'Hidden issues → Identified problems with recommendations',
+
+  before: `
+    <div style="display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 260px;">
+      <div style="padding: 12px; background: #f8f8f8; border-radius: 4px;">
+        <div style="font-size: 12px; color: #aaa;">Welcome message</div>
+        <button style="margin-top: 8px; padding: 4px 8px; font-size: 11px; background: #ddd; border: none; border-radius: 2px; color: #888;">click here</button>
+      </div>
+      <div style="padding: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px;">
+        <div style="font-size: 15px; color: rgba(255,255,255,0.7);">Featured Item</div>
+      </div>
+      <div style="display: flex; gap: 4px;">
+        <button style="width: 20px; height: 20px; font-size: 10px; background: #eee; border: none;">←</button>
+        <button style="width: 20px; height: 20px; font-size: 10px; background: #eee; border: none;">→</button>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 280px;">
+      <div style="padding: 12px; background: #fff8f8; border: 1px solid #ffcccc; border-radius: 4px; position: relative;">
+        <div style="position: absolute; top: -8px; right: 8px; background: #ff4444; color: white; font-size: 9px; padding: 2px 6px; border-radius: 8px; font-weight: 600;">CONTRAST</div>
+        <div style="font-size: 12px; color: #aaa;">Welcome message</div>
+        <button style="margin-top: 8px; padding: 4px 8px; font-size: 11px; background: #ddd; border: none; border-radius: 2px; color: #888;">click here</button>
+      </div>
+      <div style="padding: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px; border: 2px solid #ff9800; position: relative;">
+        <div style="position: absolute; top: -8px; right: 8px; background: #ff9800; color: white; font-size: 9px; padding: 2px 6px; border-radius: 8px; font-weight: 600;">READABILITY</div>
+        <div style="font-size: 15px; color: rgba(255,255,255,0.7);">Featured Item</div>
+      </div>
+      <div style="display: flex; gap: 4px; padding: 4px; background: #fff3e0; border: 1px solid #ffcc80; border-radius: 4px; position: relative;">
+        <div style="position: absolute; top: -8px; right: 8px; background: #ff9800; color: white; font-size: 9px; padding: 2px 6px; border-radius: 8px; font-weight: 600;">TOUCH TARGET</div>
+        <button style="width: 20px; height: 20px; font-size: 10px; background: #eee; border: none;">←</button>
+        <button style="width: 20px; height: 20px; font-size: 10px; background: #eee; border: none;">→</button>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/bolder.js 🔗

@@ -0,0 +1,24 @@
+// Bolder command demo - shows timid design becoming bold and confident
+export default {
+  id: 'bolder',
+  caption: 'Timid design → Bold, confident design',
+  
+  before: `
+    <div style="text-align: center; padding: var(--spacing-md); max-width: 280px;">
+      <div style="font-size: 1.125rem; font-weight: 500; margin-bottom: 8px; color: var(--color-charcoal);">Introducing Our Product</div>
+      <div style="font-size: 0.875rem; color: var(--color-ash); margin-bottom: 16px;">A solution for modern teams</div>
+      <button style="padding: 8px 16px; background: var(--color-mist); color: var(--color-charcoal); border: none; border-radius: 4px; font-size: 0.875rem;">Learn More</button>
+    </div>
+  `,
+  
+  after: `
+    <div style="text-align: center; padding: var(--spacing-lg); max-width: 320px;">
+      <div style="font-family: var(--font-display); font-size: 2.5rem; font-weight: 300; font-style: italic; margin-bottom: 12px; color: var(--color-ink); line-height: 1;">Introducing Our Product</div>
+      <div style="font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.15em; color: var(--color-accent); margin-bottom: 24px;">A solution for modern teams</div>
+      <button style="padding: 14px 32px; background: var(--color-ink); color: var(--color-paper); border: none; font-size: 0.9375rem; font-weight: 500; letter-spacing: 0.02em;">Learn More</button>
+    </div>
+  `
+};
+
+
+

public/js/demos/commands/clarify.js 🔗

@@ -0,0 +1,36 @@
+// Clarify command demo - shows confusing UX copy becoming clear
+export default {
+  id: 'clarify',
+  caption: 'Confusing copy → Clear, actionable language',
+
+  before: `
+    <div style="width: 100%; max-width: 260px; display: flex; flex-direction: column; gap: 12px;">
+      <div style="padding: 12px; background: #f5f5f5; border-radius: 6px;">
+        <div style="font-size: 13px; font-weight: 600; margin-bottom: 4px;">Processing Status</div>
+        <div style="font-size: 12px; color: #666;">Your request is being processed. Please wait while we complete the operation. This may take some time depending on various factors.</div>
+      </div>
+      <div style="padding: 12px; background: #fff8e1; border-radius: 6px;">
+        <div style="font-size: 13px; font-weight: 600; margin-bottom: 4px;">⚠️ Warning</div>
+        <div style="font-size: 12px; color: #666;">Proceeding with this action may result in irreversible consequences to your data and settings configuration.</div>
+      </div>
+      <button style="padding: 10px; background: #333; color: white; border: none; border-radius: 4px; font-size: 13px;">Submit Request</button>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 260px; display: flex; flex-direction: column; gap: 12px;">
+      <div style="padding: 12px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 0.8125rem; font-weight: 600; color: var(--color-ink); margin-bottom: 4px;">Saving changes...</div>
+        <div style="font-size: 0.75rem; color: var(--color-ash);">About 10 seconds remaining</div>
+        <div style="margin-top: 8px; height: 4px; background: var(--color-mist); border-radius: 2px; overflow: hidden;">
+          <div style="width: 60%; height: 100%; background: var(--color-accent);"></div>
+        </div>
+      </div>
+      <div style="padding: 12px; background: #fef3c7; border: 1px solid #fcd34d; border-radius: 6px;">
+        <div style="font-size: 0.8125rem; font-weight: 600; color: #92400e; margin-bottom: 4px;">Delete this project?</div>
+        <div style="font-size: 0.75rem; color: #854d0e; line-height: 1.5;">This will permanently delete 23 files. You can't undo this.</div>
+      </div>
+      <button style="padding: 10px; background: var(--color-ink); color: var(--color-paper); border: none; border-radius: 6px; font-size: 0.8125rem; font-weight: 500;">Save and Continue →</button>
+    </div>
+  `
+};

public/js/demos/commands/colorize.js 🔗

@@ -0,0 +1,56 @@
+// Colorize command demo - shows monochrome becoming strategically colored
+export default {
+  id: 'colorize',
+  caption: 'Monochrome UI → Strategic, harmonious color',
+
+  before: `
+    <div style="width: 100%; max-width: 240px; padding: 16px; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 6px;">
+      <div style="font-size: 11px; color: #999; margin-bottom: 12px;">TASK OVERVIEW</div>
+      <div style="display: flex; flex-direction: column; gap: 8px;">
+        <div style="display: flex; align-items: center; gap: 8px; padding: 8px; background: white; border: 1px solid #e0e0e0; border-radius: 4px;">
+          <div style="width: 8px; height: 8px; background: #ccc; border-radius: 2px;"></div>
+          <span style="font-size: 12px; color: #666;">Design review</span>
+        </div>
+        <div style="display: flex; align-items: center; gap: 8px; padding: 8px; background: white; border: 1px solid #e0e0e0; border-radius: 4px;">
+          <div style="width: 8px; height: 8px; background: #ccc; border-radius: 2px;"></div>
+          <span style="font-size: 12px; color: #666;">Update copy</span>
+        </div>
+        <div style="display: flex; align-items: center; gap: 8px; padding: 8px; background: white; border: 1px solid #e0e0e0; border-radius: 4px;">
+          <div style="width: 8px; height: 8px; background: #ccc; border-radius: 2px;"></div>
+          <span style="font-size: 12px; color: #666;">Final QA</span>
+        </div>
+      </div>
+      <div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid #e0e0e0;">
+        <div style="font-size: 11px; color: #999;">Progress: 1 of 3</div>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 240px; padding: 16px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 8px;">
+      <div style="font-size: 0.6875rem; letter-spacing: 0.08em; color: var(--color-ash); margin-bottom: 12px;">TASK OVERVIEW</div>
+      <div style="display: flex; flex-direction: column; gap: 8px;">
+        <div style="display: flex; align-items: center; gap: 10px; padding: 10px; background: color-mix(in oklch, var(--color-accent) 8%, var(--color-paper)); border: 1px solid color-mix(in oklch, var(--color-accent) 20%, var(--color-paper)); border-radius: 6px;">
+          <div style="width: 18px; height: 18px; background: var(--color-accent); border-radius: 4px; display: flex; align-items: center; justify-content: center; color: white; font-size: 10px;">✓</div>
+          <span style="font-size: 0.8125rem; color: var(--color-ink); text-decoration: line-through; opacity: 0.6;">Design review</span>
+        </div>
+        <div style="display: flex; align-items: center; gap: 10px; padding: 10px; background: var(--color-paper); border: 2px solid var(--color-accent); border-radius: 6px;">
+          <div style="width: 18px; height: 18px; border: 2px solid var(--color-accent); border-radius: 4px; background: white;"></div>
+          <span style="font-size: 0.8125rem; color: var(--color-ink); font-weight: 500;">Update copy</span>
+        </div>
+        <div style="display: flex; align-items: center; gap: 10px; padding: 10px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+          <div style="width: 18px; height: 18px; border: 1px solid var(--color-mist); border-radius: 4px; background: white;"></div>
+          <span style="font-size: 0.8125rem; color: var(--color-ash);">Final QA</span>
+        </div>
+      </div>
+      <div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--color-mist);">
+        <div style="display: flex; align-items: center; gap: 8px;">
+          <div style="flex: 1; height: 4px; background: var(--color-mist); border-radius: 2px; overflow: hidden;">
+            <div style="width: 33%; height: 100%; background: var(--color-accent);"></div>
+          </div>
+          <span style="font-size: 0.6875rem; color: var(--color-accent); font-weight: 500;">1/3</span>
+        </div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/critique.js 🔗

@@ -0,0 +1,47 @@
+// Critique command demo - shows design/UX issues being identified
+export default {
+  id: 'critique',
+  caption: 'Confusing design → UX issues identified with fixes',
+
+  before: `
+    <div style="display: flex; flex-direction: column; gap: 10px; width: 100%; max-width: 260px; padding: 12px; background: #fafafa; border-radius: 6px;">
+      <div style="font-size: 11px; color: #666; text-transform: uppercase; letter-spacing: 0.5px;">Dashboard</div>
+      <div style="font-size: 11px; color: #666;">Welcome to your dashboard where you can manage things</div>
+      <div style="display: flex; gap: 6px;">
+        <button style="flex: 1; padding: 8px; font-size: 11px; background: #4F46E5; color: white; border: none; border-radius: 4px; font-weight: 500;">Create</button>
+        <button style="flex: 1; padding: 8px; font-size: 11px; background: #4F46E5; color: white; border: none; border-radius: 4px; font-weight: 500;">Import</button>
+        <button style="flex: 1; padding: 8px; font-size: 11px; background: #4F46E5; color: white; border: none; border-radius: 4px; font-weight: 500;">Export</button>
+        <button style="flex: 1; padding: 8px; font-size: 11px; background: #4F46E5; color: white; border: none; border-radius: 4px; font-weight: 500;">Settings</button>
+      </div>
+      <div style="padding: 10px; background: white; border: 1px solid #e5e5e5; border-radius: 4px;">
+        <div style="font-size: 10px; color: #999;">Recent Activity</div>
+        <div style="font-size: 10px; color: #999; margin-top: 4px;">No items to display at this time</div>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="display: flex; flex-direction: column; gap: 10px; width: 100%; max-width: 280px; padding: 12px; background: #fafafa; border-radius: 6px;">
+      <div style="position: relative;">
+        <div style="font-size: 11px; color: #666; text-transform: uppercase; letter-spacing: 0.5px;">Dashboard</div>
+        <div style="position: absolute; top: -6px; right: -4px; background: #7c3aed; color: white; font-size: 8px; padding: 2px 5px; border-radius: 6px; font-weight: 600;">HIERARCHY</div>
+      </div>
+      <div style="font-size: 11px; color: #666; background: #fef3c7; padding: 4px 6px; border-radius: 3px; position: relative;">
+        Welcome to your dashboard where you can manage things
+        <div style="position: absolute; top: -6px; right: -4px; background: #d97706; color: white; font-size: 8px; padding: 2px 5px; border-radius: 6px; font-weight: 600;">REDUNDANT</div>
+      </div>
+      <div style="display: flex; gap: 6px; background: #fee2e2; padding: 6px; border-radius: 4px; position: relative;">
+        <div style="position: absolute; top: -6px; right: -4px; background: #dc2626; color: white; font-size: 8px; padding: 2px 5px; border-radius: 6px; font-weight: 600;">NO PRIMARY</div>
+        <button style="flex: 1; padding: 8px; font-size: 11px; background: #4F46E5; color: white; border: none; border-radius: 4px; font-weight: 500;">Create</button>
+        <button style="flex: 1; padding: 8px; font-size: 11px; background: #4F46E5; color: white; border: none; border-radius: 4px; font-weight: 500;">Import</button>
+        <button style="flex: 1; padding: 8px; font-size: 11px; background: #4F46E5; color: white; border: none; border-radius: 4px; font-weight: 500;">Export</button>
+        <button style="flex: 1; padding: 8px; font-size: 11px; background: #4F46E5; color: white; border: none; border-radius: 4px; font-weight: 500;">Settings</button>
+      </div>
+      <div style="padding: 10px; background: white; border: 1px solid #fca5a5; border-radius: 4px; position: relative;">
+        <div style="position: absolute; top: -6px; right: -4px; background: #dc2626; color: white; font-size: 8px; padding: 2px 5px; border-radius: 6px; font-weight: 600;">DEAD END</div>
+        <div style="font-size: 10px; color: #999;">Recent Activity</div>
+        <div style="font-size: 10px; color: #999; margin-top: 4px;">No items to display at this time</div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/delight.js 🔗

@@ -0,0 +1,53 @@
+// Delight command demo - shows functional UI gaining moments of joy
+export default {
+  id: 'delight',
+  caption: 'Functional feedback → Moment of joy',
+
+  before: `
+    <div style="width: 100%; max-width: 200px; display: flex; flex-direction: column; align-items: center; gap: 16px;">
+      <div style="text-align: center;">
+        <div style="font-size: 13px; color: #666; margin-bottom: 8px;">Milestone reached</div>
+        <div style="font-size: 24px; font-weight: bold; color: #333;">100</div>
+        <div style="font-size: 12px; color: #999;">tasks completed</div>
+      </div>
+      <button style="padding: 8px 16px; background: #e0e0e0; border: none; border-radius: 4px; font-size: 12px; color: #666;">Dismiss</button>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 240px; display: flex; flex-direction: column; align-items: center; gap: 16px; position: relative;">
+      <div style="position: absolute; top: -20px; left: 50%; transform: translateX(-50%); display: flex; gap: 4px;">
+        <span style="animation: confettiFall 1s ease-out 0.0s both; font-size: 16px;">🎊</span>
+        <span style="animation: confettiFall 1s ease-out 0.1s both; font-size: 14px;">✨</span>
+        <span style="animation: confettiFall 1s ease-out 0.2s both; font-size: 16px;">🎉</span>
+        <span style="animation: confettiFall 1s ease-out 0.15s both; font-size: 14px;">✨</span>
+        <span style="animation: confettiFall 1s ease-out 0.25s both; font-size: 16px;">🎊</span>
+      </div>
+      <div style="text-align: center; animation: celebratePop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;">
+        <div style="font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-accent); margin-bottom: 8px; font-weight: 600;">Milestone unlocked!</div>
+        <div style="font-family: var(--font-display); font-size: 3.5rem; font-weight: 300; font-style: italic; color: var(--color-ink); line-height: 1;">100</div>
+        <div style="font-size: 0.875rem; color: var(--color-ash); margin-top: 4px;">tasks completed</div>
+      </div>
+      <div style="display: flex; align-items: center; gap: 6px; padding: 8px 16px; background: color-mix(in oklch, var(--color-accent) 10%, var(--color-paper)); border-radius: 20px; animation: badgeSlide 0.5s ease-out 0.3s both;">
+        <span style="font-size: 14px;">🏆</span>
+        <span style="font-size: 0.75rem; font-weight: 600; color: var(--color-accent);">Centurion Badge Earned</span>
+      </div>
+    </div>
+    <style>
+      @keyframes confettiFall {
+        0% { opacity: 0; transform: translateY(-20px) rotate(0deg); }
+        50% { opacity: 1; }
+        100% { opacity: 0; transform: translateY(40px) rotate(180deg); }
+      }
+      @keyframes celebratePop {
+        0% { opacity: 0; transform: scale(0.5); }
+        70% { transform: scale(1.05); }
+        100% { opacity: 1; transform: scale(1); }
+      }
+      @keyframes badgeSlide {
+        from { opacity: 0; transform: translateY(10px); }
+        to { opacity: 1; transform: translateY(0); }
+      }
+    </style>
+  `
+};

public/js/demos/commands/distill.js 🔗

@@ -0,0 +1,47 @@
+// Distill command demo - shows cluttered UI becoming minimal
+export default {
+  id: 'distill',
+  caption: 'Cluttered interface → Essential elements only',
+
+  before: `
+    <div style="width: 100%; max-width: 280px; padding: 12px; background: #f5f5f5; border: 1px solid #ddd; border-radius: 6px;">
+      <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid #ddd;">
+        <span style="font-size: 12px; color: #666;">Dashboard</span>
+        <div style="display: flex; gap: 4px;">
+          <button style="padding: 2px 6px; font-size: 10px; background: #e0e0e0; border: none; border-radius: 2px;">⚙️</button>
+          <button style="padding: 2px 6px; font-size: 10px; background: #e0e0e0; border: none; border-radius: 2px;">🔔</button>
+          <button style="padding: 2px 6px; font-size: 10px; background: #e0e0e0; border: none; border-radius: 2px;">❓</button>
+          <button style="padding: 2px 6px; font-size: 10px; background: #e0e0e0; border: none; border-radius: 2px;">⋮</button>
+        </div>
+      </div>
+      <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 6px;">
+        <div style="padding: 8px; background: white; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 10px; color: #666;">Revenue<br><b>$12,345</b></div>
+        <div style="padding: 8px; background: white; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 10px; color: #666;">Users<br><b>1,234</b></div>
+        <div style="padding: 8px; background: white; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 10px; color: #666;">Growth<br><b>+12%</b></div>
+        <div style="padding: 8px; background: white; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 10px; color: #666;">Bounce<br><b>32%</b></div>
+      </div>
+      <div style="margin-top: 8px; padding: 8px; background: #e3f2fd; border-radius: 4px; font-size: 10px; color: #1976d2;">📊 View detailed analytics →</div>
+      <div style="margin-top: 4px; padding: 8px; background: #fff3e0; border-radius: 4px; font-size: 10px; color: #e65100;">🎯 Set up goals →</div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 260px; padding: 20px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 8px;">
+      <div style="font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-ash); margin-bottom: 16px;">This Month</div>
+      <div style="margin-bottom: 20px;">
+        <div style="font-family: var(--font-display); font-size: 2.5rem; font-weight: 300; color: var(--color-ink); line-height: 1;">$12,345</div>
+        <div style="font-size: 0.8125rem; color: #22c55e; margin-top: 4px;">↑ 12% from last month</div>
+      </div>
+      <div style="display: flex; gap: 24px;">
+        <div>
+          <div style="font-size: 1.25rem; font-weight: 500; color: var(--color-ink);">1,234</div>
+          <div style="font-size: 0.6875rem; color: var(--color-ash);">Active users</div>
+        </div>
+        <div>
+          <div style="font-size: 1.25rem; font-weight: 500; color: var(--color-ink);">68%</div>
+          <div style="font-size: 0.6875rem; color: var(--color-ash);">Retention</div>
+        </div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/extract.js 🔗

@@ -0,0 +1,62 @@
+// Extract command demo - shows patterns being identified and documented
+export default {
+  id: 'extract',
+  caption: 'Scattered styles → Documented design tokens',
+
+  before: `
+    <div style="width: 100%; max-width: 280px; display: flex; flex-direction: column; gap: 6px; font-family: monospace; font-size: 11px;">
+      <div style="padding: 8px; background: #1e1e1e; color: #d4d4d4; border-radius: 4px; overflow: hidden;">
+        <span style="color: #9cdcfe;">padding</span><span style="color: #d4d4d4;">: </span><span style="color: #ce9178;">12px 16px</span><span style="color: #d4d4d4;">;</span>
+      </div>
+      <div style="padding: 8px; background: #1e1e1e; color: #d4d4d4; border-radius: 4px; overflow: hidden;">
+        <span style="color: #9cdcfe;">padding</span><span style="color: #d4d4d4;">: </span><span style="color: #ce9178;">18px 24px</span><span style="color: #d4d4d4;">;</span>
+      </div>
+      <div style="padding: 8px; background: #1e1e1e; color: #d4d4d4; border-radius: 4px; overflow: hidden;">
+        <span style="color: #9cdcfe;">color</span><span style="color: #d4d4d4;">: </span><span style="color: #ce9178;">#3b82f6</span><span style="color: #d4d4d4;">;</span>
+      </div>
+      <div style="padding: 8px; background: #1e1e1e; color: #d4d4d4; border-radius: 4px; overflow: hidden;">
+        <span style="color: #9cdcfe;">color</span><span style="color: #d4d4d4;">: </span><span style="color: #ce9178;">#3a80f5</span><span style="color: #d4d4d4;">;</span>
+      </div>
+      <div style="padding: 8px; background: #1e1e1e; color: #d4d4d4; border-radius: 4px; overflow: hidden;">
+        <span style="color: #9cdcfe;">font-size</span><span style="color: #d4d4d4;">: </span><span style="color: #ce9178;">14px</span><span style="color: #d4d4d4;">;</span>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 280px; display: flex; flex-direction: column; gap: 8px;">
+      <div style="font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-ash); margin-bottom: 4px;">Design Tokens</div>
+      <div style="padding: 10px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 10px; color: var(--color-ash); margin-bottom: 6px;">SPACING</div>
+        <div style="display: flex; gap: 8px;">
+          <div style="text-align: center;">
+            <div style="width: 24px; height: 8px; background: var(--color-accent); border-radius: 2px; margin-bottom: 4px;"></div>
+            <span style="font-family: monospace; font-size: 9px; color: var(--color-charcoal);">sm</span>
+          </div>
+          <div style="text-align: center;">
+            <div style="width: 32px; height: 8px; background: var(--color-accent); border-radius: 2px; margin-bottom: 4px;"></div>
+            <span style="font-family: monospace; font-size: 9px; color: var(--color-charcoal);">md</span>
+          </div>
+          <div style="text-align: center;">
+            <div style="width: 48px; height: 8px; background: var(--color-accent); border-radius: 2px; margin-bottom: 4px;"></div>
+            <span style="font-family: monospace; font-size: 9px; color: var(--color-charcoal);">lg</span>
+          </div>
+        </div>
+      </div>
+      <div style="padding: 10px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 10px; color: var(--color-ash); margin-bottom: 6px;">COLORS</div>
+        <div style="display: flex; gap: 6px;">
+          <div style="width: 24px; height: 24px; background: var(--color-ink); border-radius: 4px;" title="ink"></div>
+          <div style="width: 24px; height: 24px; background: var(--color-charcoal); border-radius: 4px;" title="charcoal"></div>
+          <div style="width: 24px; height: 24px; background: var(--color-accent); border-radius: 4px;" title="accent"></div>
+          <div style="width: 24px; height: 24px; background: var(--color-mist); border-radius: 4px; border: 1px solid var(--color-ash);" title="mist"></div>
+        </div>
+      </div>
+      <div style="padding: 10px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 10px; color: var(--color-ash); margin-bottom: 6px;">TYPOGRAPHY</div>
+        <div style="font-family: var(--font-display); font-size: 16px; font-style: italic; color: var(--color-ink);">Display</div>
+        <div style="font-size: 12px; color: var(--color-charcoal);">Body text</div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/harden.js 🔗

@@ -0,0 +1,42 @@
+// Harden command demo - shows error handling and edge cases
+export default {
+  id: 'harden',
+  caption: 'Fragile UI → Robust error handling',
+
+  before: `
+    <div style="width: 100%; max-width: 260px; display: flex; flex-direction: column; gap: 8px;">
+      <div style="padding: 16px; background: #fff0f0; border-radius: 6px; text-align: center;">
+        <div style="font-size: 24px; margin-bottom: 8px;">⚠️</div>
+        <div style="font-size: 14px; color: #cc0000; font-weight: 500;">Error</div>
+        <div style="font-size: 12px; color: #888; margin-top: 4px;">Something went wrong</div>
+        <button style="margin-top: 12px; padding: 6px 12px; background: #ddd; border: none; border-radius: 4px; font-size: 12px; color: #666;">OK</button>
+      </div>
+      <div style="padding: 12px; background: #f5f5f5; border-radius: 6px;">
+        <div style="font-size: 11px; color: #999; margin-bottom: 4px;">Form Input</div>
+        <input type="text" value="invalid@" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; box-sizing: border-box;">
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 260px; display: flex; flex-direction: column; gap: 8px;">
+      <div style="padding: 16px; background: color-mix(in oklch, var(--color-accent) 8%, var(--color-paper)); border: 1px solid color-mix(in oklch, var(--color-accent) 20%, var(--color-paper)); border-radius: 8px;">
+        <div style="display: flex; align-items: flex-start; gap: 12px;">
+          <div style="width: 32px; height: 32px; background: var(--color-accent); border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0;">
+            <span style="color: white; font-size: 16px;">!</span>
+          </div>
+          <div style="flex: 1;">
+            <div style="font-size: 0.875rem; font-weight: 600; color: var(--color-ink); margin-bottom: 4px;">Connection failed</div>
+            <div style="font-size: 0.8125rem; color: var(--color-charcoal); line-height: 1.4;">Unable to reach the server. Check your internet connection and try again.</div>
+            <button style="margin-top: 12px; padding: 8px 16px; background: var(--color-accent); color: white; border: none; border-radius: 6px; font-size: 0.8125rem; font-weight: 500; cursor: pointer;">Retry Connection</button>
+          </div>
+        </div>
+      </div>
+      <div style="padding: 12px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 11px; color: var(--color-ash); margin-bottom: 4px;">Email Address</div>
+        <input type="text" value="invalid@" style="width: 100%; padding: 8px; border: 2px solid #ef4444; border-radius: 4px; font-size: 13px; box-sizing: border-box; background: #fef2f2;">
+        <div style="font-size: 11px; color: #ef4444; margin-top: 4px;">Please enter a complete email address</div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/index.js 🔗

@@ -0,0 +1,41 @@
+// Command demos registry
+
+import animate from "./animate.js";
+import bolder from "./bolder.js";
+import audit from "./audit.js";
+import critique from "./critique.js";
+import polish from "./polish.js";
+import optimize from "./optimize.js";
+import harden from "./harden.js";
+import clarify from "./clarify.js";
+import quieter from "./quieter.js";
+import distill from "./distill.js";
+import colorize from "./colorize.js";
+import delight from "./delight.js";
+import adapt from "./adapt.js";
+import typeset from "./typeset.js";
+import layout from "./layout.js";
+import overdrive from "./overdrive.js";
+
+export const commandDemos = {
+	bolder,
+	animate,
+	audit,
+	critique,
+	polish,
+	optimize,
+	harden,
+	clarify,
+	quieter,
+	distill,
+	colorize,
+	delight,
+	adapt,
+	typeset,
+	layout,
+	overdrive,
+};
+
+export function getCommandDemo(commandId) {
+	return commandDemos[commandId] || null;
+}

public/js/demos/commands/layout.js 🔗

@@ -0,0 +1,47 @@
+// Layout command demo - shows monotonous equal spacing becoming rhythmic and intentional
+export default {
+  id: 'layout',
+  caption: 'Equal spacing everywhere → Intentional rhythm and hierarchy',
+
+  before: `
+    <div style="width: 100%; max-width: 240px; padding: 16px;">
+      <div style="text-align: center; margin-bottom: 16px;">
+        <div style="font-size: 14px; font-weight: bold; color: #333;">Team Members</div>
+      </div>
+      <div style="display: flex; flex-direction: column; gap: 16px;">
+        <div style="padding: 16px; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 8px; text-align: center;">
+          <div style="width: 32px; height: 32px; background: #ddd; border-radius: 50%; margin: 0 auto 8px;"></div>
+          <div style="font-size: 13px; color: #333;">Alice Chen</div>
+          <div style="font-size: 12px; color: #888;">Designer</div>
+        </div>
+        <div style="padding: 16px; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 8px; text-align: center;">
+          <div style="width: 32px; height: 32px; background: #ddd; border-radius: 50%; margin: 0 auto 8px;"></div>
+          <div style="font-size: 13px; color: #333;">Bob Park</div>
+          <div style="font-size: 12px; color: #888;">Engineer</div>
+        </div>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 240px; padding: 16px; font-family: 'Instrument Sans', sans-serif;">
+      <div style="font-size: 0.8125rem; font-weight: 600; color: var(--color-ink); margin-bottom: 16px;">Team Members</div>
+      <div style="display: flex; flex-direction: column; gap: 6px;">
+        <div style="display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--color-mist);">
+          <div style="width: 28px; height: 28px; background: var(--color-accent); border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: white; font-size: 11px; font-weight: 600;">AC</div>
+          <div style="flex: 1;">
+            <div style="font-size: 0.8125rem; font-weight: 500; color: var(--color-ink);">Alice Chen</div>
+            <div style="font-size: 0.6875rem; color: var(--color-ash);">Designer</div>
+          </div>
+        </div>
+        <div style="display: flex; align-items: center; gap: 12px; padding: 10px 0;">
+          <div style="width: 28px; height: 28px; background: color-mix(in oklch, var(--color-accent) 60%, var(--color-ink)); border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: white; font-size: 11px; font-weight: 600;">BP</div>
+          <div style="flex: 1;">
+            <div style="font-size: 0.8125rem; font-weight: 500; color: var(--color-ink);">Bob Park</div>
+            <div style="font-size: 0.6875rem; color: var(--color-ash);">Engineer</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/normalize.js 🔗

@@ -0,0 +1,42 @@
+// Normalize command demo - shows inconsistent styles becoming systematic
+export default {
+  id: 'normalize',
+  caption: 'Inconsistent styles → Systematic design tokens',
+  
+  before: `
+    <div style="display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 260px;">
+      <div style="padding: 12px 16px; background: #f0f0f0; border-radius: 6px;">
+        <div style="font-size: 14px; font-weight: 600; margin-bottom: 4px;">Card One</div>
+        <div style="font-size: 13px; color: #888;">Some description text here</div>
+      </div>
+      <div style="padding: 18px 12px; background: #e8e8e8; border-radius: 12px;">
+        <div style="font-size: 16px; font-weight: 500; margin-bottom: 8px;">Card Two</div>
+        <div style="font-size: 14px; color: #666;">Different spacing and styles</div>
+      </div>
+      <div style="padding: 10px 20px; background: #f5f5f5; border-radius: 4px;">
+        <div style="font-size: 15px; font-weight: 700; margin-bottom: 2px;">Card Three</div>
+        <div style="font-size: 12px; color: #999;">Yet another variation</div>
+      </div>
+    </div>
+  `,
+  
+  after: `
+    <div style="display: flex; flex-direction: column; gap: var(--spacing-sm); width: 100%; max-width: 260px;">
+      <div style="padding: var(--spacing-md); background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; color: var(--color-ink);">Card One</div>
+        <div style="font-size: 0.8125rem; color: var(--color-ash);">Consistent description text</div>
+      </div>
+      <div style="padding: var(--spacing-md); background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; color: var(--color-ink);">Card Two</div>
+        <div style="font-size: 0.8125rem; color: var(--color-ash);">Same spacing and styles</div>
+      </div>
+      <div style="padding: var(--spacing-md); background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; color: var(--color-ink);">Card Three</div>
+        <div style="font-size: 0.8125rem; color: var(--color-ash);">Unified design system</div>
+      </div>
+    </div>
+  `
+};
+
+
+

public/js/demos/commands/onboard.js 🔗

@@ -0,0 +1,31 @@
+// Onboard command demo - shows empty state becoming helpful onboarding
+export default {
+  id: 'onboard',
+  caption: 'Empty state → Guided onboarding experience',
+
+  before: `
+    <div style="width: 100%; max-width: 260px; padding: 24px; background: #f5f5f5; border: 1px solid #ddd; border-radius: 6px; text-align: center;">
+      <div style="font-size: 32px; opacity: 0.3; margin-bottom: 8px;">📁</div>
+      <div style="font-size: 14px; color: #999;">No items found</div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 280px; padding: 24px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 12px; text-align: center;">
+      <div style="width: 64px; height: 64px; margin: 0 auto 16px; background: color-mix(in oklch, var(--color-accent) 15%, var(--color-paper)); border-radius: 16px; display: flex; align-items: center; justify-content: center;">
+        <span style="font-size: 28px;">✨</span>
+      </div>
+      <div style="font-family: var(--font-display); font-size: 1.25rem; font-weight: 400; color: var(--color-ink); margin-bottom: 8px;">Create your first project</div>
+      <div style="font-size: 0.8125rem; color: var(--color-ash); line-height: 1.5; margin-bottom: 20px;">Projects help you organize your work. Start with a template or blank canvas.</div>
+      <div style="display: flex; flex-direction: column; gap: 8px;">
+        <button style="padding: 12px; background: var(--color-ink); color: var(--color-paper); border: none; border-radius: 8px; font-size: 0.875rem; font-weight: 500; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px;">
+          <span>+</span> New Project
+        </button>
+        <button style="padding: 10px; background: transparent; color: var(--color-charcoal); border: 1px solid var(--color-mist); border-radius: 8px; font-size: 0.8125rem; cursor: pointer;">Browse Templates</button>
+      </div>
+      <div style="margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--color-mist);">
+        <div style="font-size: 0.6875rem; color: var(--color-ash);">Need help? <span style="color: var(--color-accent); cursor: pointer;">Watch a quick tutorial →</span></div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/optimize.js 🔗

@@ -0,0 +1,57 @@
+// Optimize command demo - shows performance improvements
+export default {
+  id: 'optimize',
+  caption: 'Heavy, slow UI → Lightweight, performant',
+
+  before: `
+    <div style="width: 100%; max-width: 260px; display: flex; flex-direction: column; gap: 8px;">
+      <div style="padding: 12px; background: #f5f5f5; border-radius: 6px;">
+        <div style="font-size: 11px; color: #999; margin-bottom: 4px;">BUNDLE SIZE</div>
+        <div style="height: 8px; background: #e0e0e0; border-radius: 4px; overflow: hidden;">
+          <div style="width: 95%; height: 100%; background: linear-gradient(90deg, #ff6b6b, #ee5a5a);"></div>
+        </div>
+        <div style="font-size: 12px; color: #ff6b6b; margin-top: 4px; font-weight: 600;">847 KB</div>
+      </div>
+      <div style="padding: 12px; background: #f5f5f5; border-radius: 6px;">
+        <div style="font-size: 11px; color: #999; margin-bottom: 4px;">RENDER TIME</div>
+        <div style="height: 8px; background: #e0e0e0; border-radius: 4px; overflow: hidden;">
+          <div style="width: 80%; height: 100%; background: linear-gradient(90deg, #ffa726, #ff9800);"></div>
+        </div>
+        <div style="font-size: 12px; color: #ff9800; margin-top: 4px; font-weight: 600;">2.4s</div>
+      </div>
+      <div style="padding: 12px; background: #f5f5f5; border-radius: 6px;">
+        <div style="font-size: 11px; color: #999; margin-bottom: 4px;">LAYOUT SHIFTS</div>
+        <div style="height: 8px; background: #e0e0e0; border-radius: 4px; overflow: hidden;">
+          <div style="width: 60%; height: 100%; background: linear-gradient(90deg, #ffa726, #ff9800);"></div>
+        </div>
+        <div style="font-size: 12px; color: #ff9800; margin-top: 4px; font-weight: 600;">CLS: 0.18</div>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 260px; display: flex; flex-direction: column; gap: 8px;">
+      <div style="padding: 12px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 11px; color: var(--color-ash); margin-bottom: 4px;">BUNDLE SIZE</div>
+        <div style="height: 8px; background: var(--color-mist); border-radius: 4px; overflow: hidden;">
+          <div style="width: 25%; height: 100%; background: linear-gradient(90deg, #22c55e, #16a34a);"></div>
+        </div>
+        <div style="font-size: 12px; color: #22c55e; margin-top: 4px; font-weight: 600;">124 KB <span style="color: var(--color-ash); font-weight: 400;">(-85%)</span></div>
+      </div>
+      <div style="padding: 12px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 11px; color: var(--color-ash); margin-bottom: 4px;">RENDER TIME</div>
+        <div style="height: 8px; background: var(--color-mist); border-radius: 4px; overflow: hidden;">
+          <div style="width: 15%; height: 100%; background: linear-gradient(90deg, #22c55e, #16a34a);"></div>
+        </div>
+        <div style="font-size: 12px; color: #22c55e; margin-top: 4px; font-weight: 600;">0.3s <span style="color: var(--color-ash); font-weight: 400;">(-88%)</span></div>
+      </div>
+      <div style="padding: 12px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
+        <div style="font-size: 11px; color: var(--color-ash); margin-bottom: 4px;">LAYOUT SHIFTS</div>
+        <div style="height: 8px; background: var(--color-mist); border-radius: 4px; overflow: hidden;">
+          <div style="width: 5%; height: 100%; background: linear-gradient(90deg, #22c55e, #16a34a);"></div>
+        </div>
+        <div style="font-size: 12px; color: #22c55e; margin-top: 4px; font-weight: 600;">CLS: 0.02 <span style="color: var(--color-ash); font-weight: 400;">(excellent)</span></div>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/overdrive.js 🔗

@@ -0,0 +1,342 @@
+// Overdrive command demo - laser-etched signature on a premium dark surface
+// Laser effect adapted from pbakaus/shaders laser-precision
+
+export default {
+  id: 'overdrive',
+  caption: 'Static flat card → Laser-etched signature effect',
+
+  before: `
+    <div style="width: 100%; height: 100%; min-height: 200px; display: flex; align-items: center; justify-content: center; background: #f5f5f5; font-family: system-ui, sans-serif;">
+      <div style="text-align: center; padding: 20px;">
+        <div style="font-size: 12px; color: #666; font-style: italic; line-height: 1.6; max-width: 220px; margin-bottom: 16px;">It's time to spark your imagination. Welcome to the Impeccable Community.</div>
+        <div style="font-size: 12px; color: #aaa;">Paul Bakaus</div>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <canvas class="od-burn" style="position: absolute; inset: 0; width: 100%; height: 100%; background: #0e0d0b;"></canvas>
+    <canvas class="od-sparks" style="position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none;"></canvas>
+    <div style="position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px; pointer-events: none; text-align: center;">
+      <p style="font-family: 'Cormorant Garamond', serif; font-size: 1.1rem; font-style: italic; font-weight: 400; color: rgba(240,230,210,0.85); line-height: 1.5; max-width: 260px; margin: 0 0 24px;">It's time to spark your imagination.<br>Welcome to the Impeccable Community.</p>
+    </div>
+  `,
+
+  init(container) {
+    const burnCanvas = container.querySelector('.od-burn');
+    const sparkCanvas = container.querySelector('.od-sparks');
+    if (!burnCanvas || !sparkCanvas) return;
+
+    const rect = burnCanvas.parentElement.getBoundingClientRect();
+    const dpr = Math.min(window.devicePixelRatio || 1, 2);
+
+    // Size both canvases
+    for (const c of [burnCanvas, sparkCanvas]) {
+      c.width = Math.round(rect.width * dpr);
+      c.height = Math.round(rect.height * dpr);
+    }
+
+    const ctx = burnCanvas.getContext('2d');  // persistent burn trails
+    const sCtx = sparkCanvas.getContext('2d'); // cleared each frame
+    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+    sCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
+    const w = rect.width;
+    const h = rect.height;
+
+    // Fill background
+    ctx.fillStyle = '#0e0d0b';
+    ctx.fillRect(0, 0, w, h);
+
+    // ── Signature paths — two separate strokes ──
+    function buildSignaturePaths() {
+      function makePath(buildFn) {
+        const pts = [];
+        function bez(x0,y0, cx1,cy1, cx2,cy2, x1,y1, n) {
+          for (let i = 0; i <= n; i++) {
+            const t = i / n, mt = 1-t;
+            pts.push({
+              x: mt*mt*mt*x0 + 3*mt*mt*t*cx1 + 3*mt*t*t*cx2 + t*t*t*x1,
+              y: mt*mt*mt*y0 + 3*mt*mt*t*cy1 + 3*mt*t*t*cy2 + t*t*t*y1
+            });
+          }
+        }
+        buildFn(bez);
+        return pts;
+      }
+
+      const paul = makePath(bez => {
+        // P
+        bez(6,44, 5,32, 4,18, 8,8, 14);
+        bez(8,8, 16,5, 26,7, 26,16, 12);
+        bez(26,16, 26,22, 18,26, 14,28, 10);
+        // a
+        bez(14,28, 18,22, 23,20, 26,22, 8);
+        bez(26,22, 29,24, 28,30, 24,32, 6);
+        bez(24,32, 28,34, 30,30, 32,28, 5);
+        // u
+        bez(32,28, 34,36, 38,40, 42,32, 8);
+        bez(42,32, 44,26, 47,24, 48,28, 6);
+        // l
+        bez(48,28, 49,16, 50,6, 53,8, 10);
+        bez(53,8, 55,14, 56,28, 58,32, 8);
+      });
+
+      const bakaus = makePath(bez => {
+        // B
+        bez(66,44, 66,32, 67,16, 70,8, 14);
+        bez(70,8, 78,4, 83,10, 79,18, 12);
+        bez(79,18, 84,15, 87,24, 80,30, 12);
+        bez(80,30, 78,34, 80,36, 84,32, 5);
+        // akaus
+        bez(84,32, 89,24, 94,22, 97,26, 8);
+        bez(97,26, 99,30, 96,34, 100,30, 5);
+        bez(100,30, 101,20, 102,14, 104,16, 8);
+        bez(104,16, 106,24, 109,28, 107,32, 6);
+        bez(107,32, 105,36, 110,36, 113,30, 6);
+        bez(113,30, 118,24, 122,22, 125,28, 8);
+        bez(125,28, 128,36, 133,38, 137,30, 8);
+        bez(137,30, 139,26, 142,24, 144,28, 5);
+        bez(144,28, 154,24, 170,22, 195,28, 16);
+      });
+
+      // Scale both paths
+      const rawW = 200;
+      const scale = (w * 0.7) / rawW;
+      const ox = (w - rawW * scale) / 2;
+      const oy = h * 0.52;
+      const transform = p => ({ x: p.x * scale + ox, y: p.y * scale * 0.75 + oy });
+      return [paul.map(transform), bakaus.map(transform)];
+    }
+
+    const strokes = buildSignaturePaths();
+
+    // Precompute lengths for each stroke
+    function computeLengths(pts) {
+      const lens = [];
+      let total = 0;
+      for (let i = 1; i < pts.length; i++) {
+        const dx = pts[i].x - pts[i-1].x, dy = pts[i].y - pts[i-1].y;
+        const l = Math.sqrt(dx*dx + dy*dy);
+        lens.push(l); total += l;
+      }
+      return { lens, total };
+    }
+
+    const strokeData = strokes.map(pts => {
+      const { lens, total } = computeLengths(pts);
+      return { pts, lens, total };
+    });
+
+    function posAtStroke(stroke, dist) {
+      let d = 0;
+      for (let i = 0; i < stroke.lens.length; i++) {
+        if (d + stroke.lens[i] >= dist) {
+          const t = stroke.lens[i] > 0 ? (dist - d) / stroke.lens[i] : 0;
+          const p0 = stroke.pts[i], p1 = stroke.pts[i+1];
+          return { x: p0.x + (p1.x - p0.x) * t, y: p0.y + (p1.y - p0.y) * t };
+        }
+        d += stroke.lens[i];
+      }
+      return stroke.pts[stroke.pts.length - 1];
+    }
+
+    const totalLength = strokeData.reduce((s, d) => s + d.total, 0);
+
+    // ── State ──
+    let currentStroke = 0;
+    let drawnLength = 0;
+    const drawSpeed = totalLength / 3.0;
+    let prevTip = strokes[0][0];
+    let sparks = [];
+    let phase = 'drawing'; // drawing, lifting, holding, fading
+    let phaseTimer = 0;
+    let lastTime = 0;
+
+    // Track drawn points per stroke for smooth rendering
+    const allDrawnStrokes = [[], []];
+
+    function drawBurnTrail() {
+      ctx.lineCap = 'round';
+      ctx.lineJoin = 'round';
+
+      function strokeSmooth(pts, color, width) {
+        if (pts.length < 2) return;
+        ctx.beginPath();
+        ctx.moveTo(pts[0].x, pts[0].y);
+        for (let i = 1; i < pts.length - 1; i++) {
+          const mx = (pts[i].x + pts[i+1].x) / 2;
+          const my = (pts[i].y + pts[i+1].y) / 2;
+          ctx.quadraticCurveTo(pts[i].x, pts[i].y, mx, my);
+        }
+        ctx.lineTo(pts[pts.length-1].x, pts[pts.length-1].y);
+        ctx.strokeStyle = color;
+        ctx.lineWidth = width;
+        ctx.stroke();
+      }
+
+      // Draw all accumulated strokes
+      for (const pts of allDrawnStrokes) {
+        strokeSmooth(pts, 'rgba(180, 100, 30, 0.12)', 5);
+        strokeSmooth(pts, 'rgba(220, 140, 50, 0.3)', 2.5);
+        strokeSmooth(pts, 'rgba(255, 210, 130, 0.7)', 1.2);
+        strokeSmooth(pts, 'rgba(255, 248, 235, 0.6)', 0.4);
+      }
+    }
+
+    function emitSparks(x, y, count) {
+      for (let i = 0; i < count; i++) {
+        const angle = Math.random() * Math.PI * 2;
+        const speed = 50 + Math.random() * 140;
+        sparks.push({
+          x, y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed,
+          life: 0.15 + Math.random() * 0.35, maxLife: 0.15 + Math.random() * 0.35,
+          size: 0.3 + Math.random() * 1.0, bright: Math.random() > 0.4
+        });
+      }
+    }
+
+    function draw(timestamp) {
+      if (!document.contains(burnCanvas)) return;
+
+      // Actual frame delta time
+      if (!lastTime) lastTime = timestamp;
+      const dt = Math.min(0.05, (timestamp - lastTime) / 1000);
+      lastTime = timestamp;
+
+      switch (phase) {
+        case 'drawing': {
+          const sd = strokeData[currentStroke];
+          drawnLength += drawSpeed * dt;
+          if (drawnLength >= sd.total) {
+            drawnLength = sd.total;
+            emitSparks(prevTip.x, prevTip.y, 6);
+            if (currentStroke < strokes.length - 1) {
+              // Lift — pause briefly before starting next stroke
+              phase = 'lifting';
+              phaseTimer = 0;
+            } else {
+              phase = 'holding';
+              phaseTimer = 0;
+            }
+          }
+          const tip = posAtStroke(sd, drawnLength);
+          allDrawnStrokes[currentStroke].push({ x: tip.x, y: tip.y });
+          prevTip = tip;
+          if (Math.random() < 0.4) emitSparks(tip.x, tip.y, 1);
+          // Redraw full smooth trail
+          ctx.fillStyle = '#0e0d0b';
+          ctx.fillRect(0, 0, w, h);
+          drawBurnTrail();
+          break;
+        }
+
+        case 'lifting':
+          phaseTimer += dt;
+          if (phaseTimer >= 0.25) {
+            currentStroke++;
+            drawnLength = 0;
+            prevTip = strokes[currentStroke][0];
+            phase = 'drawing';
+            phaseTimer = 0;
+          }
+          break;
+
+        case 'holding':
+          phaseTimer += dt;
+          if (phaseTimer >= 3.5) { phase = 'fading'; phaseTimer = 0; }
+          break;
+
+        case 'fading':
+          phaseTimer += dt;
+          ctx.fillStyle = 'rgba(14, 13, 11, 0.04)';
+          ctx.fillRect(0, 0, w, h);
+          if (phaseTimer >= 2.0) {
+            ctx.fillStyle = '#0e0d0b';
+            ctx.fillRect(0, 0, w, h);
+            currentStroke = 0; drawnLength = 0;
+            prevTip = strokes[0][0];
+            sparks = [];
+            allDrawnStrokes[0].length = 0;
+            allDrawnStrokes[1].length = 0;
+            phase = 'drawing'; phaseTimer = 0;
+          }
+          break;
+      }
+
+      // Update sparks
+      for (let i = sparks.length - 1; i >= 0; i--) {
+        const s = sparks[i];
+        s.x += s.vx * dt; s.y += s.vy * dt;
+        s.vx *= 0.94; s.vy *= 0.94; s.vy += 100 * dt;
+        s.life -= dt;
+        if (s.life <= 0) sparks.splice(i, 1);
+      }
+
+      // Draw sparks + tip on overlay (cleared each frame)
+      sCtx.clearRect(0, 0, w, h);
+
+      for (const s of sparks) {
+        const t = s.life / s.maxLife;
+        const r = s.size * (0.3 + t * 0.7);
+        // Spark trail
+        const speed = Math.sqrt(s.vx*s.vx + s.vy*s.vy);
+        if (speed > 20) {
+          const tl = speed * 0.01;
+          sCtx.beginPath();
+          sCtx.moveTo(s.x, s.y);
+          sCtx.lineTo(s.x - s.vx/speed * tl, s.y - s.vy/speed * tl);
+          sCtx.strokeStyle = s.bright
+            ? `rgba(255,255,240,${(t*0.4).toFixed(3)})`
+            : `rgba(255,180,60,${(t*0.3).toFixed(3)})`;
+          sCtx.lineWidth = r * 0.5;
+          sCtx.lineCap = 'round';
+          sCtx.stroke();
+        }
+        sCtx.beginPath();
+        sCtx.arc(s.x, s.y, r, 0, Math.PI * 2);
+        sCtx.fillStyle = s.bright
+          ? `rgba(255,255,255,${(t*0.85).toFixed(3)})`
+          : `rgba(255,200,80,${(t*0.75).toFixed(3)})`;
+        sCtx.fill();
+      }
+
+      // Draw laser tip on overlay
+      if (phase === 'drawing' && drawnLength < strokeData[currentStroke].total) {
+        const tip = posAtStroke(strokeData[currentStroke], drawnLength);
+        const fl = 0.85 + Math.random() * 0.15;
+
+        // Wide heat bloom
+        const g0 = sCtx.createRadialGradient(tip.x, tip.y, 0, tip.x, tip.y, 35);
+        g0.addColorStop(0, `rgba(255,100,20,${0.15*fl})`);
+        g0.addColorStop(0.4, `rgba(200,60,10,${0.05*fl})`);
+        g0.addColorStop(1, 'rgba(150,40,10,0)');
+        sCtx.fillStyle = g0; sCtx.beginPath(); sCtx.arc(tip.x, tip.y, 35, 0, Math.PI*2); sCtx.fill();
+
+        // Amber corona
+        const g1 = sCtx.createRadialGradient(tip.x, tip.y, 0, tip.x, tip.y, 16);
+        g1.addColorStop(0, `rgba(255,180,60,${0.45*fl})`);
+        g1.addColorStop(0.5, `rgba(255,140,40,${0.15*fl})`);
+        g1.addColorStop(1, 'rgba(200,80,20,0)');
+        sCtx.fillStyle = g1; sCtx.beginPath(); sCtx.arc(tip.x, tip.y, 16, 0, Math.PI*2); sCtx.fill();
+
+        // White-hot core
+        const g2 = sCtx.createRadialGradient(tip.x, tip.y, 0, tip.x, tip.y, 6);
+        g2.addColorStop(0, `rgba(255,255,255,${0.95*fl})`);
+        g2.addColorStop(0.3, `rgba(255,250,240,${0.7*fl})`);
+        g2.addColorStop(0.6, `rgba(255,220,160,${0.3*fl})`);
+        g2.addColorStop(1, 'rgba(255,180,80,0)');
+        sCtx.fillStyle = g2; sCtx.beginPath(); sCtx.arc(tip.x, tip.y, 6, 0, Math.PI*2); sCtx.fill();
+
+        // Overexposed center
+        const g3 = sCtx.createRadialGradient(tip.x, tip.y, 0, tip.x, tip.y, 2.5);
+        g3.addColorStop(0, `rgba(255,255,255,${fl})`);
+        g3.addColorStop(1, 'rgba(255,255,255,0)');
+        sCtx.fillStyle = g3; sCtx.beginPath(); sCtx.arc(tip.x, tip.y, 2.5, 0, Math.PI*2); sCtx.fill();
+      }
+
+      requestAnimationFrame(draw);
+    }
+
+    requestAnimationFrame(draw);
+  }
+};

public/js/demos/commands/polish.js 🔗

@@ -0,0 +1,33 @@
+// Polish command demo - shows rough UI becoming refined
+export default {
+  id: 'polish',
+  caption: 'Rough edges → Refined, pixel-perfect details',
+
+  before: `
+    <div style="width: 100%; max-width: 240px; padding: 16px; background: #f5f5f5; border: 1px solid #ddd; border-radius: 4px;">
+      <div style="font-size: 16px; font-weight: bold; margin-bottom: 8px;">User Profile</div>
+      <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px;">
+        <div style="width: 40px; height: 40px; background: #ccc; border-radius: 50%;"></div>
+        <div>
+          <div style="font-size: 14px;">John Doe</div>
+          <div style="font-size: 12px; color: #888;">Developer</div>
+        </div>
+      </div>
+      <button style="width: 100%; padding: 8px; background: #333; color: white; border: none; border-radius: 4px; font-size: 13px;">Edit Profile</button>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 240px; padding: 20px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
+      <div style="font-family: var(--font-display); font-size: 1.125rem; font-weight: 400; margin-bottom: 16px; color: var(--color-ink);">User Profile</div>
+      <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
+        <div style="width: 48px; height: 48px; background: var(--color-ink); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--color-paper); font-weight: 600; font-size: 1.125rem;">JD</div>
+        <div>
+          <div style="font-size: 0.9375rem; font-weight: 500; color: var(--color-ink);">John Doe</div>
+          <div style="font-size: 0.75rem; color: var(--color-ash); letter-spacing: 0.02em;">Developer</div>
+        </div>
+      </div>
+      <button style="width: 100%; padding: 10px; background: var(--color-ink); color: var(--color-paper); border: none; border-radius: 6px; font-size: 0.875rem; font-weight: 500; letter-spacing: 0.01em; cursor: pointer;">Edit Profile</button>
+    </div>
+  `
+};

public/js/demos/commands/quieter.js 🔗

@@ -0,0 +1,28 @@
+// Quieter command demo - shows loud design becoming calm
+export default {
+  id: 'quieter',
+  caption: 'Overwhelming design → Calm, focused interface',
+
+  before: `
+    <div style="width: 100%; max-width: 280px; padding: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px; text-align: center;">
+      <div style="font-size: 24px; margin-bottom: 8px;">🎉✨🚀</div>
+      <div style="font-size: 18px; font-weight: 800; color: #ffeb3b; text-transform: uppercase; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); margin-bottom: 8px;">AMAZING DEAL!</div>
+      <div style="font-size: 14px; color: white; margin-bottom: 12px;">Don't miss out on this INCREDIBLE opportunity!!!</div>
+      <div style="display: flex; gap: 8px; justify-content: center;">
+        <button style="padding: 10px 16px; background: #ff4081; color: white; border: none; border-radius: 20px; font-weight: bold; font-size: 14px; text-transform: uppercase; box-shadow: 0 4px 15px rgba(255,64,129,0.4);">BUY NOW!!</button>
+        <button style="padding: 10px 16px; background: #00e676; color: white; border: none; border-radius: 20px; font-weight: bold; font-size: 14px; text-transform: uppercase;">LEARN MORE</button>
+      </div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 280px; padding: 24px; background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 8px; text-align: center;">
+      <div style="font-family: var(--font-display); font-size: 1.5rem; font-weight: 300; font-style: italic; color: var(--color-ink); margin-bottom: 8px;">Limited Time Offer</div>
+      <div style="font-size: 0.875rem; color: var(--color-ash); margin-bottom: 20px; line-height: 1.5;">Save 20% on annual plans. Offer ends Friday.</div>
+      <div style="display: flex; gap: 12px; justify-content: center;">
+        <button style="padding: 12px 24px; background: var(--color-ink); color: var(--color-paper); border: none; border-radius: 6px; font-size: 0.875rem; font-weight: 500;">View Plans</button>
+        <button style="padding: 12px 24px; background: transparent; color: var(--color-charcoal); border: 1px solid var(--color-mist); border-radius: 6px; font-size: 0.875rem;">Maybe Later</button>
+      </div>
+    </div>
+  `
+};

public/js/demos/commands/typeset.js 🔗

@@ -0,0 +1,23 @@
+// Typeset command demo - shows flat, hierarchyless text becoming intentional typography
+export default {
+  id: 'typeset',
+  caption: 'No type hierarchy → Clear, intentional typography',
+
+  before: `
+    <div style="width: 100%; max-width: 240px; padding: 16px; font-family: Arial, sans-serif;">
+      <div style="font-size: 14px; font-weight: bold; color: #444; margin-bottom: 8px;">Project Update</div>
+      <div style="font-size: 14px; color: #444; margin-bottom: 8px;">Q1 Design Sprint</div>
+      <div style="font-size: 14px; color: #444; line-height: 1.4; margin-bottom: 8px;">The team completed the redesign of the dashboard. All components have been reviewed and approved by stakeholders.</div>
+      <div style="font-size: 14px; color: #444;">Updated 2 hours ago</div>
+    </div>
+  `,
+
+  after: `
+    <div style="width: 100%; max-width: 240px; padding: 16px; font-family: 'Instrument Sans', sans-serif;">
+      <div style="font-size: 0.625rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-ash); margin-bottom: 6px;">Project Update</div>
+      <div style="font-family: 'Cormorant Garamond', serif; font-size: 1.5rem; font-weight: 600; color: var(--color-ink); line-height: 1.1; margin-bottom: 12px;">Q1 Design Sprint</div>
+      <p style="font-size: 0.8125rem; color: color-mix(in oklch, var(--color-ink) 65%, transparent); line-height: 1.65; margin: 0 0 14px; max-width: 30ch;">The team completed the redesign of the dashboard. All components reviewed and approved.</p>
+      <div style="font-size: 0.6875rem; color: var(--color-ash); font-variant-numeric: tabular-nums;">Updated 2 hours ago</div>
+    </div>
+  `
+};

public/js/demos/index.js 🔗

@@ -0,0 +1,6 @@
+// Main demos registry - exports all demos
+export { commandDemos, getCommandDemo } from "./commands/index.js";
+export { getSkillDemo, skillDemos } from "./skills/index.js";
+
+
+

public/js/demos/skills/color-and-contrast.js 🔗

@@ -0,0 +1,68 @@
+// Color and Contrast skill demos
+export default {
+  id: 'color-and-contrast',
+  tabs: [
+    {
+      id: 'palette',
+      label: 'Color Harmony',
+      caption: 'Clashing colors vs harmonious palette',
+      beforeClass: 'color-demo color-palette-before',
+      afterClass: 'color-demo color-palette-after',
+      before: `
+        <div class="color-demo color-palette-before">
+          <div class="color-swatch swatch-1"></div>
+          <div class="color-swatch swatch-2"></div>
+          <div class="color-swatch swatch-3"></div>
+          <div class="color-swatch swatch-4"></div>
+          <div class="color-swatch swatch-5"></div>
+          <div class="color-card">
+            <span class="card-title">Title</span>
+            <span class="card-subtitle">Subtitle</span>
+            <button class="card-btn">Action</button>
+          </div>
+        </div>
+      `,
+      after: null // Uses CSS class toggle
+    },
+    {
+      id: 'accent',
+      label: 'Strategic Accent',
+      caption: 'Monochrome monotony vs strategic accent',
+      beforeClass: 'color-demo color-accent-before',
+      afterClass: 'color-demo color-accent-after',
+      before: `
+        <div class="color-demo color-accent-before">
+          <div class="color-accent-card">
+            <div class="color-accent-title">Premium Plan</div>
+            <div class="color-accent-text">Unlock all features and get priority support.</div>
+            <button class="color-accent-btn">Upgrade Now</button>
+          </div>
+        </div>
+      `,
+      after: null // Uses CSS class toggle
+    },
+    {
+      id: 'contrast',
+      label: 'Contrast Ratios',
+      caption: 'Accessibility failures vs WCAG compliance',
+      hasToggle: false, // Static comparison
+      before: `
+        <div class="color-demo color-contrast-static">
+          <div class="contrast-example contrast-fail">
+            <span class="contrast-badge">Fails WCAG</span>
+            <div class="contrast-text">Hard to Read</div>
+            <div class="contrast-ratio">2.5:1</div>
+          </div>
+          <div class="contrast-example contrast-pass">
+            <span class="contrast-badge">Passes AAA</span>
+            <div class="contrast-text">Easy to Read</div>
+            <div class="contrast-ratio">12.6:1</div>
+          </div>
+        </div>
+      `
+    }
+  ]
+};
+
+
+

public/js/demos/skills/index.js 🔗

@@ -0,0 +1,26 @@
+// Skill demos registry
+
+import colorAndContrast from "./color-and-contrast.js";
+import interactionDesign from "./interaction-design.js";
+import motionDesign from "./motion-design.js";
+import responsiveDesign from "./responsive-design.js";
+import spatialDesign from "./spatial-design.js";
+import typography from "./typography.js";
+import uxWriting from "./ux-writing.js";
+
+export const skillDemos = {
+	"ux-writing": uxWriting,
+	"spatial-design": spatialDesign,
+	"motion-design": motionDesign,
+	typography: typography,
+	"interaction-design": interactionDesign,
+	"color-and-contrast": colorAndContrast,
+	"responsive-design": responsiveDesign,
+};
+
+export function getSkillDemo(skillId) {
+	return skillDemos[skillId] || null;
+}
+
+
+

public/js/demos/skills/interaction-design.js 🔗

@@ -0,0 +1,79 @@
+// Interaction Design skill demos
+export default {
+  id: 'interaction-design',
+  tabs: [
+    {
+      id: 'states',
+      label: 'Button States',
+      caption: 'Missing states vs complete interaction feedback',
+      // This demo shows both states side-by-side, no toggle needed
+      hasToggle: false,
+      before: `
+        <div class="int-demo int-states-demo">
+          <div class="int-state-row">
+            <span class="int-state-label">Poor</span>
+            <button class="int-btn int-btn-poor">Click Me</button>
+          </div>
+          <div class="int-state-row">
+            <span class="int-state-label">Good</span>
+            <button class="int-btn int-btn-good">Click Me</button>
+          </div>
+        </div>
+      `
+    },
+    {
+      id: 'affordance',
+      label: 'Affordances',
+      caption: 'Unclear actions vs obvious clickability',
+      beforeClass: 'int-demo int-affordance-before',
+      afterClass: 'int-demo int-affordance-after',
+      before: `
+        <div class="int-demo int-affordance-before">
+          <div class="int-aff-item int-aff-poor">
+            <span>Learn more</span>
+          </div>
+          <div class="int-aff-item int-aff-poor">
+            <span>Settings</span>
+          </div>
+        </div>
+      `,
+      after: null // Uses CSS class toggle
+    },
+    {
+      id: 'feedback',
+      label: 'Feedback',
+      caption: 'Silent actions vs immediate confirmation',
+      before: `
+        <div class="int-demo int-feedback-before">
+          <button class="int-fb-btn int-fb-silent">
+            <svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
+          </button>
+          <span class="int-fb-label">Click — nothing happens</span>
+        </div>
+      `,
+      after: `
+        <div class="int-demo int-feedback-after">
+          <button class="int-fb-btn int-fb-active" data-action="like">
+            <svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
+          </button>
+          <span class="int-fb-label">Click to try!</span>
+        </div>
+      `,
+      onAfterRender: () => {
+        // Setup interactive like button
+        document.querySelectorAll('.int-fb-active[data-action="like"]').forEach(btn => {
+          btn.addEventListener('click', () => {
+            btn.classList.toggle('liked');
+            const label = btn.nextElementSibling;
+            if (label) {
+              label.textContent = btn.classList.contains('liked') ? 'Liked!' : 'Click to try!';
+            }
+          });
+        });
+      }
+    }
+  ]
+};
+
+
+

public/js/demos/skills/motion-design.js 🔗

@@ -0,0 +1,69 @@
+// Motion Design skill demos
+export default {
+  id: 'motion-design',
+  tabs: [
+    {
+      id: 'stagger',
+      label: 'Staggered Reveal',
+      caption: 'Instant appearance vs orchestrated reveal',
+      before: `
+        <div class="motion-demo motion-stagger-demo">
+          <div class="motion-list-item"><span class="motion-dot"></span>Dashboard</div>
+          <div class="motion-list-item"><span class="motion-dot"></span>Analytics</div>
+          <div class="motion-list-item"><span class="motion-dot"></span>Settings</div>
+          <div class="motion-list-item"><span class="motion-dot"></span>Profile</div>
+        </div>
+      `,
+      after: null, // CSS animation triggered by data-state
+      onToggle: (viewport, isAfter) => {
+        if (isAfter) {
+          // Re-trigger animation by cloning elements
+          const items = viewport.querySelectorAll('.motion-list-item');
+          items.forEach(item => {
+            const clone = item.cloneNode(true);
+            item.parentNode.replaceChild(clone, item);
+          });
+        }
+      }
+    },
+    {
+      id: 'micro',
+      label: 'Micro-interactions',
+      caption: 'Static button vs responsive feedback',
+      before: `
+        <div class="motion-demo motion-micro-demo">
+          <button class="motion-btn motion-btn-before">Add to Cart</button>
+        </div>
+      `,
+      after: `
+        <div class="motion-demo motion-micro-demo">
+          <button class="motion-btn motion-btn-after">Add to Cart</button>
+        </div>
+      `
+    },
+    {
+      id: 'transition',
+      label: 'State Changes',
+      caption: 'Jarring change vs smooth transition',
+      before: `
+        <div class="motion-demo motion-transition-demo">
+          <div class="motion-card motion-card-before">
+            <div class="motion-card-icon">📦</div>
+            <div class="motion-card-text">Order Placed</div>
+          </div>
+        </div>
+      `,
+      after: `
+        <div class="motion-demo motion-transition-demo">
+          <div class="motion-card motion-card-after">
+            <div class="motion-card-icon">✓</div>
+            <div class="motion-card-text">Order Confirmed</div>
+          </div>
+        </div>
+      `
+    }
+  ]
+};
+
+
+

public/js/demos/skills/responsive-design.js 🔗

@@ -0,0 +1,91 @@
+// Responsive Design skill demos
+export default {
+  id: 'responsive-design',
+  tabs: [
+    {
+      id: 'touch',
+      label: 'Touch Targets',
+      caption: 'Tiny targets vs accessible touch areas',
+      hasToggle: false, // Static comparison
+      before: `
+        <div class="resp-demo resp-touch-demo">
+          <div class="resp-touch-row">
+            <span class="resp-label">Too Small</span>
+            <div class="resp-touch-targets resp-touch-bad">
+              <button>×</button>
+              <button>−</button>
+              <button>+</button>
+            </div>
+          </div>
+          <div class="resp-touch-row">
+            <span class="resp-label">Accessible</span>
+            <div class="resp-touch-targets resp-touch-good">
+              <button>×</button>
+              <button>−</button>
+              <button>+</button>
+            </div>
+          </div>
+        </div>
+      `
+    },
+    {
+      id: 'fluid',
+      label: 'Fluid Layout',
+      caption: 'Fixed breakage vs fluid adaptation',
+      hasToggle: false, // Static comparison
+      before: `
+        <div class="resp-demo resp-fluid-demo">
+          <div class="resp-fluid-container">
+            <div class="resp-fluid-fixed">
+              <span>Fixed 400px</span>
+              <div class="resp-fluid-bar" style="width: 400px; max-width: 100%;"></div>
+            </div>
+            <div class="resp-fluid-adaptive">
+              <span>Fluid 80%</span>
+              <div class="resp-fluid-bar" style="width: 80%;"></div>
+            </div>
+          </div>
+        </div>
+      `
+    },
+    {
+      id: 'adapt',
+      label: 'Adaptive Content',
+      caption: 'Same layout vs optimized for context',
+      hasToggle: false, // Static comparison
+      before: `
+        <div class="resp-demo resp-adapt-demo">
+          <div class="resp-device resp-device-mobile">
+            <div class="resp-device-screen">
+              <div class="resp-block resp-header"></div>
+              <div class="resp-block resp-content"></div>
+            </div>
+            <span>Mobile</span>
+          </div>
+          <div class="resp-device resp-device-tablet">
+            <div class="resp-device-screen">
+              <div class="resp-block resp-header"></div>
+              <div class="resp-block-row">
+                <div class="resp-block resp-content"></div>
+                <div class="resp-block resp-content"></div>
+              </div>
+            </div>
+            <span>Tablet</span>
+          </div>
+          <div class="resp-device resp-device-desktop">
+            <div class="resp-device-screen">
+              <div class="resp-block-row">
+                <div class="resp-block resp-sidebar"></div>
+                <div class="resp-block resp-content"></div>
+              </div>
+            </div>
+            <span>Desktop</span>
+          </div>
+        </div>
+      `
+    }
+  ]
+};
+
+
+

public/js/demos/skills/spatial-design.js 🔗

@@ -0,0 +1,62 @@
+// Spatial Design skill demos
+export default {
+  id: 'spatial-design',
+  tabs: [
+    {
+      id: 'grid',
+      label: 'Grid Systems',
+      caption: 'Chaotic placement vs intentional grid alignment',
+      before: `
+        <div class="spatial-demo spatial-grid-before">
+          <div class="spatial-card-item" style="width: 45%;">Card One</div>
+          <div class="spatial-card-item" style="width: 30%;">Card Two</div>
+          <div class="spatial-card-item" style="width: 55%;">Card Three</div>
+          <div class="spatial-card-item" style="width: 25%;">Card Four</div>
+        </div>
+      `,
+      after: `
+        <div class="spatial-demo spatial-grid-after">
+          <div class="spatial-card-item">Card One</div>
+          <div class="spatial-card-item">Card Two</div>
+          <div class="spatial-card-item">Card Three</div>
+          <div class="spatial-card-item">Card Four</div>
+        </div>
+      `
+    },
+    {
+      id: 'hierarchy',
+      label: 'Visual Weight',
+      caption: 'Equal weight vs clear visual priority',
+      beforeClass: 'spatial-demo spatial-hierarchy-before',
+      afterClass: 'spatial-demo spatial-hierarchy-after',
+      before: `
+        <div class="spatial-demo spatial-hierarchy-before">
+          <div class="spatial-h-title">Welcome Back</div>
+          <div class="spatial-h-subtitle">Dashboard</div>
+          <div class="spatial-h-cta">View Reports</div>
+          <div class="spatial-h-link">Settings</div>
+        </div>
+      `,
+      after: null // Uses CSS class toggle
+    },
+    {
+      id: 'whitespace',
+      label: 'Breathing Room',
+      caption: 'Cramped elements vs comfortable spacing',
+      beforeClass: 'spatial-demo spatial-whitespace-before',
+      afterClass: 'spatial-demo spatial-whitespace-after',
+      before: `
+        <div class="spatial-demo spatial-whitespace-before">
+          <div class="spatial-ws-title">Premium Plan</div>
+          <div class="spatial-ws-price">$29/mo</div>
+          <div class="spatial-ws-features">Unlimited projects • Priority support • Advanced analytics</div>
+          <button class="spatial-ws-btn">Upgrade Now</button>
+        </div>
+      `,
+      after: null // Uses CSS class toggle
+    }
+  ]
+};
+
+
+

public/js/demos/skills/typography.js 🔗

@@ -0,0 +1,38 @@
+// Typography skill demos
+export default {
+  id: 'typography',
+  tabs: [
+    {
+      id: 'pairing',
+      label: 'Font Pairing',
+      caption: 'Generic system fonts vs distinctive pairing',
+      beforeClass: 'typo-demo typo-pairing-before',
+      afterClass: 'typo-demo typo-pairing-after',
+      before: `
+        <div class="typo-demo typo-pairing-before">
+          <div class="typo-heading">Welcome to the Future</div>
+          <div class="typo-body">Experience innovation like never before with our cutting-edge platform designed for modern teams.</div>
+        </div>
+      `,
+      after: null // Uses CSS class toggle
+    },
+    {
+      id: 'hierarchy',
+      label: 'Scale & Hierarchy',
+      caption: 'Flat sizing vs dramatic scale contrast',
+      beforeClass: 'typo-demo typo-hierarchy-before',
+      afterClass: 'typo-demo typo-hierarchy-after',
+      before: `
+        <div class="typo-demo typo-hierarchy-before">
+          <div class="typo-h1">Article Title</div>
+          <div class="typo-meta">Published January 2025</div>
+          <div class="typo-p">This is the body text of the article containing the main content and ideas.</div>
+        </div>
+      `,
+      after: null // Uses CSS class toggle
+    }
+  ]
+};
+
+
+

public/js/demos/skills/ux-writing.js 🔗

@@ -0,0 +1,71 @@
+// UX Writing skill demos
+export default {
+  id: 'ux-writing',
+  tabs: [
+    {
+      id: 'errors',
+      label: 'Error Messages',
+      caption: 'Technical jargon vs human, actionable guidance',
+      before: `
+        <div class="uxw-demo uxw-error-before">
+          <div class="uxw-error-icon">⚠</div>
+          <div class="uxw-error-title">Error 403</div>
+          <div class="uxw-error-text">Access denied. Authentication failure occurred.</div>
+        </div>
+      `,
+      after: `
+        <div class="uxw-demo uxw-error-after">
+          <div class="uxw-error-icon">🔐</div>
+          <div class="uxw-error-title">You don't have access</div>
+          <div class="uxw-error-text">Your session may have expired. Sign in again to continue.</div>
+          <div class="uxw-error-action">Sign in →</div>
+        </div>
+      `
+    },
+    {
+      id: 'buttons',
+      label: 'Button Labels',
+      caption: 'Vague labels vs clear, specific actions',
+      before: `
+        <div class="uxw-demo uxw-buttons-before">
+          <div class="uxw-button-context">Delete account permanently?</div>
+          <div class="uxw-button-row">
+            <button class="uxw-btn uxw-btn-primary">Submit</button>
+            <button class="uxw-btn uxw-btn-secondary">Cancel</button>
+          </div>
+        </div>
+      `,
+      after: `
+        <div class="uxw-demo uxw-buttons-after">
+          <div class="uxw-button-context">Delete account permanently?</div>
+          <div class="uxw-button-row">
+            <button class="uxw-btn uxw-btn-danger">Delete My Account</button>
+            <button class="uxw-btn uxw-btn-secondary">Keep Account</button>
+          </div>
+        </div>
+      `
+    },
+    {
+      id: 'empty',
+      label: 'Empty States',
+      caption: 'Blank nothing vs helpful, encouraging guidance',
+      before: `
+        <div class="uxw-demo uxw-empty-before">
+          <div class="uxw-empty-icon">📁</div>
+          <div class="uxw-empty-title">No items</div>
+        </div>
+      `,
+      after: `
+        <div class="uxw-demo uxw-empty-after">
+          <div class="uxw-empty-icon">📝</div>
+          <div class="uxw-empty-title">No projects yet</div>
+          <div class="uxw-empty-text">Create your first project to get started.</div>
+          <div class="uxw-empty-action"><button class="uxw-btn uxw-btn-primary">Create Project</button></div>
+        </div>
+      `
+    }
+  ]
+};
+
+
+

public/js/effects/hero-shader.js 🔗

@@ -0,0 +1,57 @@
+import {
+  ShaderMount,
+  meshGradientFragmentShader,
+  getShaderColorFromString,
+  ShaderFitOptions
+} from '@paper-design/shaders';
+
+export function initHeroShader() {
+  const container = document.getElementById('hero-shader');
+  if (!container) return;
+
+  // Respect user's motion preferences
+  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
+    container.style.display = 'none';
+    return;
+  }
+
+  // Liquid ink mesh gradient
+  const shader = new ShaderMount(
+    container,
+    meshGradientFragmentShader,
+    {
+      // Colors - liquid ink effect (grayscale tones for editorial look)
+      u_colors: [
+        getShaderColorFromString('#ffffff'),
+        getShaderColorFromString('#e8e4df'),
+        getShaderColorFromString('#b5b0a8'),
+        getShaderColorFromString('#1a1a1a'),
+      ],
+      u_colorsCount: 4,
+
+      // Effect parameters
+      u_distortion: 1.0,
+      u_swirl: 0.2,
+      u_grainMixer: 0,
+      u_grainOverlay: 0,
+
+      // Sizing uniforms (required by mesh gradient)
+      u_fit: ShaderFitOptions.cover,
+      u_scale: 1,
+      u_rotation: 0,
+      u_offsetX: 0,
+      u_offsetY: 0,
+      u_originX: 0.5,
+      u_originY: 0.5,
+      u_worldWidth: 0,
+      u_worldHeight: 0,
+    },
+    undefined,
+    1.0 // speed
+  );
+
+  // Cleanup
+  window.addEventListener('beforeunload', () => {
+    shader.dispose();
+  });
+}

public/js/effects/liquid-canvas.js 🔗

@@ -0,0 +1,165 @@
+export function initHeroEffect() {
+	const canvas = document.getElementById("hero-canvas");
+	if (!canvas) return;
+
+	// Respect user's motion preferences
+	if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
+		canvas.style.display = 'none';
+		return;
+	}
+
+	const ctx = canvas.getContext("2d");
+	let width, height;
+	let points = [];
+	let gap = 50; // Grid gap
+	const mouse = { x: -1000, y: -1000, radius: 150 }; // Moderate radius
+	let animationId;
+
+	// Physics params - Elegant & Fluid
+	const friction = 0.9; // Higher friction = less slippery
+	const ease = 0.1; // Standard spring
+	const forceMultiplier = 3; // Subtle push, not a splash
+
+	class Point {
+		constructor(x, y) {
+			this.x = x;
+			this.y = y;
+			this.ox = x; // original x
+			this.oy = y; // original y
+			this.vx = 0;
+			this.vy = 0;
+		}
+
+		update() {
+			// Mouse interaction
+			const dx = mouse.x - this.x;
+			const dy = mouse.y - this.y;
+			const dist = Math.sqrt(dx * dx + dy * dy);
+			const force = Math.max(0, (mouse.radius - dist) / mouse.radius);
+
+			if (force > 0) {
+				const angle = Math.atan2(dy, dx);
+				// Gentle push
+				this.vx -= Math.cos(angle) * force * forceMultiplier;
+				this.vy -= Math.sin(angle) * force * forceMultiplier;
+			}
+
+			// Spring back to original position
+			this.vx += (this.ox - this.x) * ease;
+			this.vy += (this.oy - this.y) * ease;
+
+			// Friction
+			this.vx *= friction;
+			this.vy *= friction;
+
+			// Update position
+			this.x += this.vx;
+			this.y += this.vy;
+		}
+	}
+
+	function resize() {
+		const dpr = window.devicePixelRatio || 1;
+		const rect = canvas.getBoundingClientRect();
+		width = rect.width;
+		height = rect.height;
+		canvas.width = width * dpr;
+		canvas.height = height * dpr;
+		ctx.scale(dpr, dpr);
+		
+		initGrid();
+	}
+
+	function initGrid() {
+		points = [];
+		// Responsive gap
+		gap = width < 768 ? 40 : 50;
+		
+		const cols = Math.ceil(width / gap);
+		const rows = Math.ceil(height / gap);
+
+		for (let i = 0; i <= cols; i++) {
+			for (let j = 0; j <= rows; j++) {
+				points.push(new Point(i * gap, j * gap));
+			}
+		}
+	}
+
+	function draw() {
+		ctx.clearRect(0, 0, width, height);
+		
+		// Update points
+		points.forEach(p => p.update());
+
+		// Draw grid lines
+		ctx.beginPath();
+		ctx.strokeStyle = "rgba(100, 40, 50, 0.06)"; // Very subtle base
+		ctx.lineWidth = 1;
+
+		const cols = Math.ceil(width / gap) + 1;
+		
+		for (let i = 0; i < points.length; i++) {
+			const p = points[i];
+			
+			// Draw Horizontal
+			if ((i + 1) % cols !== 0 && i + 1 < points.length) {
+				const next = points[i + 1];
+				// Use Bezier for fluid curves instead of straight lines
+				const xc = (p.x + next.x) / 2;
+				const yc = (p.y + next.y) / 2;
+				ctx.moveTo(p.x, p.y);
+				// ctx.quadraticCurveTo(p.x, p.y, xc, yc); // Slightly more expensive but smoother? 
+				// Actually straight lines with high enough density look fine and are faster.
+				// Let's stick to lineTo for performance, the points themselves move smoothly.
+				ctx.lineTo(next.x, next.y);
+			}
+
+			// Draw Vertical
+			if (i + cols < points.length) {
+				const next = points[i + cols];
+				ctx.moveTo(p.x, p.y);
+				ctx.lineTo(next.x, next.y);
+			}
+		}
+		ctx.stroke();
+
+		animationId = requestAnimationFrame(draw);
+	}
+
+	function handleMouseMove(e) {
+		const rect = canvas.getBoundingClientRect();
+		mouse.x = e.clientX - rect.left;
+		mouse.y = e.clientY - rect.top;
+	}
+	
+	function handleMouseLeave() {
+		mouse.x = -1000;
+		mouse.y = -1000;
+	}
+
+	window.addEventListener("resize", resize);
+	canvas.parentElement.addEventListener("mousemove", handleMouseMove);
+	canvas.parentElement.addEventListener("mouseleave", handleMouseLeave);
+
+	resize();
+	draw();
+
+	// Cleanup
+	const observer = new IntersectionObserver((entries) => {
+		entries.forEach((entry) => {
+			if (entry.isIntersecting) {
+				if (!animationId) draw();
+			} else {
+				if (animationId) {
+					cancelAnimationFrame(animationId);
+					animationId = null;
+				}
+			}
+		});
+	});
+
+	observer.observe(canvas);
+}
+
+
+

public/js/effects/split-compare.js 🔗

@@ -0,0 +1,229 @@
+// ============================================
+// SPLIT COMPARE - Reusable before/after split-screen effect
+// ============================================
+
+/**
+ * Initialize split comparison effect on a container
+ * @param {HTMLElement} container - The container element with .split-container inside
+ * @param {Object} options - Configuration options
+ */
+export function initSplitCompare(container, options = {}) {
+	const {
+		defaultPosition = 70,
+		skewAngle = 10, // Degrees — matches CSS skewX(-10deg) on .split-divider
+		lerpSpeed = 0.15,
+		animationThreshold = 40, // Re-trigger animations when crossing this threshold
+		onCrossThreshold = null // Callback when crossing threshold toward "after" side
+	} = options;
+
+	const splitContainer = container.querySelector('.split-container');
+	const splitAfter = container.querySelector('.split-after');
+	const splitDivider = container.querySelector('.split-divider');
+
+	if (!splitContainer || !splitAfter || !splitDivider) return null;
+
+	// Compute skewOffset from container dimensions so clip-path angle matches CSS skewX
+	const tanAngle = Math.tan(skewAngle * Math.PI / 180);
+	let skewOffset = 8; // fallback
+
+	function recalcSkewOffset() {
+		const rect = splitContainer.getBoundingClientRect();
+		if (rect.width > 0 && rect.height > 0) {
+			skewOffset = 50 * rect.height * tanAngle / rect.width;
+		}
+	}
+	recalcSkewOffset();
+
+	const resizeObserver = new ResizeObserver(recalcSkewOffset);
+	resizeObserver.observe(splitContainer);
+
+	let minPosition = -skewOffset;
+	let maxPosition = 100 + skewOffset;
+	if (options.minPosition != null) minPosition = options.minPosition;
+	if (options.maxPosition != null) maxPosition = options.maxPosition;
+
+	let isHovering = false;
+	let currentX = defaultPosition;
+	let targetX = defaultPosition;
+	let animationId = null;
+	let wasAboveThreshold = defaultPosition > animationThreshold;
+
+	function updateSplit(percent) {
+		const minPos = options.minPosition != null ? minPosition : -skewOffset;
+		const maxPos = options.maxPosition != null ? maxPosition : 100 + skewOffset;
+		const clampedX = Math.max(minPos, Math.min(maxPos, percent));
+
+		// Check if we crossed the threshold toward the "after" side (moving left)
+		const isAboveThreshold = clampedX > animationThreshold;
+		if (wasAboveThreshold && !isAboveThreshold) {
+			// Crossed threshold - re-trigger animations
+			retriggerAnimations();
+			if (onCrossThreshold) onCrossThreshold(clampedX);
+		}
+		wasAboveThreshold = isAboveThreshold;
+
+		// Angled clip-path matching divider's skewX — offset computed from actual dimensions
+		splitAfter.style.clipPath = `polygon(${clampedX + skewOffset}% 0%, 100% 0%, 100% 100%, ${clampedX - skewOffset}% 100%)`;
+		splitDivider.style.left = `${clampedX}%`;
+	}
+
+	function retriggerAnimations() {
+		// Re-trigger CSS animations in the "after" content.
+		// If there's a canvas (e.g. overdrive shader), we can't clone-and-replace
+		// because that destroys JS-driven animations. In that case, retrigger
+		// individual elements. Otherwise, use the fast clone approach.
+		const afterContent = splitAfter.querySelector('.split-content');
+		if (!afterContent) return;
+
+		const hasCanvas = afterContent.querySelector('canvas, .od-burn, .od-sparks');
+		if (hasCanvas) {
+			// Safe path: retrigger CSS animations individually, skip canvas
+			afterContent.querySelectorAll('*').forEach(el => {
+				if (el.tagName === 'CANVAS') return;
+				const anim = getComputedStyle(el).animationName;
+				if (anim && anim !== 'none') {
+					el.style.animation = 'none';
+					el.offsetHeight;
+					el.style.animation = '';
+				}
+			});
+		} else {
+			// Fast path: clone and replace to restart all CSS animations
+			const clone = afterContent.cloneNode(true);
+			afterContent.parentNode.replaceChild(clone, afterContent);
+		}
+	}
+
+	function animate() {
+		const diff = targetX - currentX;
+		if (Math.abs(diff) > 0.1) {
+			currentX += diff * lerpSpeed;
+			updateSplit(currentX);
+			animationId = requestAnimationFrame(animate);
+		} else {
+			currentX = targetX;
+			updateSplit(currentX);
+			animationId = null;
+		}
+	}
+
+	function startAnimation() {
+		if (!animationId) {
+			animationId = requestAnimationFrame(animate);
+		}
+	}
+
+	function handleMouseEnter() {
+		isHovering = true;
+	}
+
+	function handleMouseLeave() {
+		isHovering = false;
+		targetX = defaultPosition;
+		startAnimation();
+	}
+
+	function handleMouseMove(e) {
+		if (isHovering) {
+			const rect = splitContainer.getBoundingClientRect();
+			const range = 100 + 2 * skewOffset;
+			targetX = ((e.clientX - rect.left) / rect.width) * range - skewOffset;
+			startAnimation();
+		}
+	}
+
+	let touchStartX = 0;
+	let touchStartY = 0;
+	let isDragging = false;
+	const DRAG_THRESHOLD = 10; // Minimum horizontal movement to start dragging
+
+	function handleTouchStart(e) {
+		const touch = e.touches[0];
+		touchStartX = touch.clientX;
+		touchStartY = touch.clientY;
+		isDragging = false;
+		isHovering = true;
+	}
+
+	function handleTouchEnd() {
+		isHovering = false;
+		isDragging = false;
+		targetX = defaultPosition;
+		startAnimation();
+	}
+
+	function handleTouchMove(e) {
+		const touch = e.touches[0];
+		const deltaX = Math.abs(touch.clientX - touchStartX);
+		const deltaY = Math.abs(touch.clientY - touchStartY);
+
+		// Only start dragging if horizontal movement is greater than vertical
+		// This allows vertical scrolling to pass through
+		if (!isDragging) {
+			if (deltaX > DRAG_THRESHOLD && deltaX > deltaY) {
+				isDragging = true;
+			} else if (deltaY > DRAG_THRESHOLD) {
+				// User is scrolling vertically, don't interfere
+				return;
+			} else {
+				// Not enough movement yet
+				return;
+			}
+		}
+
+		// Only prevent default when actively dragging horizontally
+		if (isDragging) {
+			e.preventDefault();
+			const rect = splitContainer.getBoundingClientRect();
+			targetX = ((touch.clientX - rect.left) / rect.width) * 100;
+			startAnimation();
+		}
+	}
+
+	// Use the parent container for mouse events to create a larger hit area
+	const hitArea = splitContainer.parentElement || splitContainer;
+
+	// Attach listeners — mouse events on the wider hit area
+	hitArea.addEventListener('mouseenter', handleMouseEnter);
+	hitArea.addEventListener('mouseleave', handleMouseLeave);
+	hitArea.addEventListener('mousemove', handleMouseMove);
+	splitContainer.addEventListener('touchstart', handleTouchStart);
+	splitContainer.addEventListener('touchend', handleTouchEnd);
+	splitContainer.addEventListener('touchmove', handleTouchMove, { passive: false });
+
+	// Initialize
+	updateSplit(defaultPosition);
+
+	// Return cleanup function
+	return {
+		destroy() {
+			hitArea.removeEventListener('mouseenter', handleMouseEnter);
+			hitArea.removeEventListener('mouseleave', handleMouseLeave);
+			hitArea.removeEventListener('mousemove', handleMouseMove);
+			splitContainer.removeEventListener('touchstart', handleTouchStart);
+			splitContainer.removeEventListener('touchend', handleTouchEnd);
+			splitContainer.removeEventListener('touchmove', handleTouchMove);
+			if (animationId) cancelAnimationFrame(animationId);
+			resizeObserver.disconnect();
+		},
+		setPosition(percent) {
+			targetX = percent;
+			startAnimation();
+		}
+	};
+}
+
+/**
+ * Initialize all split comparisons on the page
+ */
+export function initAllSplitCompare(selector = '.split-comparison', options = {}) {
+	const containers = document.querySelectorAll(selector);
+	const instances = [];
+
+	containers.forEach(container => {
+		const instance = initSplitCompare(container, options);
+		if (instance) instances.push(instance);
+	});
+
+	return instances;
+}

public/js/utils/reveal.js 🔗

@@ -0,0 +1,18 @@
+import { inView } from "motion";
+
+export function initScrollReveal() {
+	const revealElements = document.querySelectorAll("[data-reveal]");
+
+	revealElements.forEach((el) => {
+		inView(
+			el,
+			() => {
+				el.classList.add("revealed");
+			},
+			{ margin: "-50px" },
+		);
+	});
+}
+
+
+

public/js/utils/scroll.js 🔗

@@ -0,0 +1,99 @@
+// Instant anchor scroll - no smooth scrolling for better UX on long pages
+export function initAnchorScroll() {
+	document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
+		anchor.addEventListener("click", (e) => {
+			e.preventDefault();
+			const target = document.querySelector(anchor.getAttribute("href"));
+			if (target) {
+				// Instant jump with small offset for visual breathing room
+				const offset = 40;
+				const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
+				window.scrollTo({ top: targetPosition, behavior: 'auto' });
+			}
+		});
+	});
+}
+
+export function initHashTracking() {
+	const sections = document.querySelectorAll('section[id]');
+	if (!sections.length) return;
+
+	let currentHash = window.location.hash.slice(1) || '';
+	let ticking = false;
+
+	function updateHash() {
+		// Don't override command deep links while user is in the commands section
+		if (currentHash.startsWith('cmd-')) {
+			const cmdEl = document.getElementById(currentHash);
+			if (cmdEl) {
+				const rect = cmdEl.getBoundingClientRect();
+				// Only clear the cmd hash if user scrolled well away from commands section
+				if (rect.top > window.innerHeight * 2 || rect.bottom < -window.innerHeight) {
+					currentHash = '';
+				} else {
+					ticking = false;
+					return;
+				}
+			}
+		}
+
+		const scrollY = window.scrollY;
+		const viewportHeight = window.innerHeight;
+		const triggerPoint = scrollY + viewportHeight * 0.3;
+
+		let activeSection = '';
+
+		sections.forEach(section => {
+			const rect = section.getBoundingClientRect();
+			const sectionTop = scrollY + rect.top;
+			const sectionBottom = sectionTop + rect.height;
+
+			if (triggerPoint >= sectionTop && triggerPoint < sectionBottom) {
+				activeSection = section.id;
+			}
+		});
+
+		// Don't set #hero — it's the default state, no hash needed
+		if (activeSection === 'hero') activeSection = '';
+
+		if (activeSection !== currentHash) {
+			currentHash = activeSection;
+			if (activeSection) {
+				history.replaceState(null, '', `#${activeSection}`);
+			} else {
+				history.replaceState(null, '', window.location.pathname);
+			}
+		}
+
+		ticking = false;
+	}
+
+	window.addEventListener('scroll', () => {
+		if (!ticking) {
+			requestAnimationFrame(updateHash);
+			ticking = true;
+		}
+	}, { passive: true });
+
+	// Handle initial hash on page load - instant jump
+	if (window.location.hash) {
+		const hash = window.location.hash.slice(1);
+		const target = document.getElementById(hash);
+		if (target) {
+			currentHash = hash;
+			setTimeout(() => {
+				const offset = 40;
+				const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
+				window.scrollTo({ top: targetPosition, behavior: 'auto' });
+
+				// If it's a command deep link, activate it
+				if (hash.startsWith('cmd-') && target.classList.contains('manual-entry')) {
+					target.click();
+				}
+			}, 100);
+		}
+	} else {
+		// No hash — don't set one on initial load
+	}
+}
+

public/js/utils/theme.js 🔗

@@ -0,0 +1,56 @@
+// Theme toggle - supports light, dark, and system preference
+
+const STORAGE_KEY = 'impeccable-theme';
+
+function getStoredTheme() {
+  return localStorage.getItem(STORAGE_KEY);
+}
+
+function setStoredTheme(theme) {
+  localStorage.setItem(STORAGE_KEY, theme);
+}
+
+function applyTheme(theme) {
+  const html = document.documentElement;
+
+  // Remove both classes first
+  html.classList.remove('light', 'dark');
+
+  if (theme === 'light') {
+    html.classList.add('light');
+  } else if (theme === 'dark') {
+    html.classList.add('dark');
+  }
+  // 'system' = no class, falls back to media query
+
+  // Update active state on buttons
+  document.querySelectorAll('.theme-toggle-btn').forEach(btn => {
+    btn.classList.toggle('active', btn.dataset.theme === theme);
+  });
+}
+
+export function initThemeToggle() {
+  const toggle = document.querySelector('.theme-toggle');
+  if (!toggle) return;
+
+  // Get stored theme or default to system
+  const storedTheme = getStoredTheme() || 'system';
+  applyTheme(storedTheme);
+
+  // Handle button clicks
+  toggle.addEventListener('click', (e) => {
+    const btn = e.target.closest('.theme-toggle-btn');
+    if (!btn) return;
+
+    const theme = btn.dataset.theme;
+    setStoredTheme(theme);
+    applyTheme(theme);
+  });
+
+  // Listen for system preference changes (only matters when in 'system' mode)
+  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
+    if (getStoredTheme() === 'system' || !getStoredTheme()) {
+      applyTheme('system');
+    }
+  });
+}

public/privacy.html 🔗

@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Privacy Policy - Impeccable</title>
+  <meta name="robots" content="noindex">
+  <link rel="icon" type="image/svg+xml" href="./favicon.svg">
+  <link rel="preconnect" href="https://fonts.googleapis.com">
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+  <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600&family=Instrument+Sans:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
+  <link rel="stylesheet" href="./css/sub-pages.css">
+  <style>
+    .privacy-content { max-width: 680px; margin: 0 auto; padding: 2rem 1.5rem 4rem; }
+    .privacy-content h1 { font-family: var(--font-display); font-size: 2.25rem; margin-bottom: 0.5rem; letter-spacing: -0.01em; }
+    .privacy-content h2 { font-size: 1.125rem; margin-top: 2rem; font-weight: 600; }
+    .privacy-content p, .privacy-content ul { color: var(--color-charcoal); margin-top: 0.5rem; }
+    .privacy-content ul { padding-left: 1.25rem; }
+    .privacy-content li { margin-top: 0.25rem; }
+    .privacy-content a { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; }
+    .privacy-content a:hover { color: var(--color-accent-hover); }
+    .privacy-content .updated { color: var(--color-ash); font-size: 0.875rem; margin-bottom: 2rem; font-style: italic; }
+  </style>
+</head>
+<body class="sub-page">
+  <!-- site-header v1 -->
+  <header class="site-header" data-site-header>
+    <a href="/" class="site-header-brand" aria-label="Impeccable home">
+      <svg class="site-header-brand-logo" viewBox="0 0 32 32" aria-hidden="true"><rect width="32" height="32" rx="6" fill="#1a1a1a"/><text x="16" y="24" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="500" fill="#f5f3ef" text-anchor="middle">/</text></svg>
+      <span class="site-header-brand-name">Impeccable</span>
+    </a>
+    <div class="site-header-right">
+      <nav class="site-header-nav" aria-label="Primary">
+        <a href="/" data-nav="home">Home</a>
+        <a href="/skills" data-nav="docs">Docs</a>
+        <a href="/anti-patterns" data-nav="anti-patterns">Anti-Patterns</a>
+        <a href="/visual-mode" data-nav="visual-mode">Visual Mode</a>
+      </nav>
+      <a href="https://github.com/pbakaus/impeccable" class="site-header-github" target="_blank" rel="noopener" aria-label="Impeccable on GitHub, 18k stars">
+        <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
+        <span class="site-header-github-label">18k</span>
+        <svg class="site-header-github-star" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2l2.76 6.36L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.24-.91L12 2z"/></svg>
+      </a>
+    </div>
+  </header>
+
+  <main class="privacy-content">
+  <h1>Privacy Policy</h1>
+  <p class="updated">Last updated: April 6, 2026</p>
+
+  <h2>What Impeccable is</h2>
+  <p>Impeccable is an open-source collection of agent skills (text files) that run locally in your AI coding tool. The skills themselves collect no data, make no network requests, and have no analytics.</p>
+
+  <h2>Website analytics</h2>
+  <p>The Impeccable website (<a href="https://impeccable.style">impeccable.style</a>) uses Google Analytics to understand traffic patterns (page views, referrers, country). No personal information is collected beyond what Google Analytics provides by default. No cookies are used for advertising.</p>
+
+  <h2>Downloads</h2>
+  <p>When you download a skill bundle from the website, we log the download event (which bundle, timestamp) for usage statistics. No personal information is attached to these logs.</p>
+
+  <h2>Claude Code Plugin</h2>
+  <p>When installed as a Claude Code plugin, Impeccable runs entirely within your local Claude Code session. No data is sent to Impeccable's servers. Anthropic's own privacy policy governs the Claude Code application itself.</p>
+
+  <h2>Chrome Extension</h2>
+  <p>The Impeccable Chrome DevTools extension runs entirely in your browser. All anti-pattern detection happens locally on the page you are inspecting. No page content, URLs, or detection results are ever sent to any external server.</p>
+  <p>The extension stores your rule preferences (which detections are enabled or disabled) using Chrome's built-in sync storage (<code>chrome.storage.sync</code>), which syncs settings across your Chrome instances via your Google account. No other data is stored or transmitted.</p>
+  <p>The extension requests the following permissions:</p>
+  <ul>
+    <li><strong>activeTab / scripting</strong> - to inject the detector script into the page you are inspecting</li>
+    <li><strong>storage</strong> - to save your rule preferences</li>
+    <li><strong>webNavigation</strong> - to re-scan automatically when you navigate to a new page</li>
+    <li><strong>Host permissions (all URLs)</strong> - so the detector can run on any website you choose to inspect</li>
+  </ul>
+
+  <h2>GitHub</h2>
+  <p>The source code is hosted on GitHub. Interactions with the repository (issues, pull requests, stars) are governed by <a href="https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement">GitHub's privacy policy</a>.</p>
+
+  <h2>Contact</h2>
+  <p>Questions about this policy? Open an issue on <a href="https://github.com/pbakaus/impeccable">GitHub</a> or reach out to <a href="https://x.com/pbakaus">@pbakaus</a>.</p>
+  </main>
+</body>
+</html>

public/robots.txt 🔗

@@ -0,0 +1,6 @@
+# robots.txt for impeccable.style
+
+User-agent: *
+Allow: /
+
+Sitemap: https://impeccable.style/sitemap.xml

public/sitemap.xml 🔗

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+  <!-- Landing + utility pages -->
+  <url>
+    <loc>https://impeccable.style/</loc>
+    <lastmod>2026-04-08</lastmod>
+    <changefreq>weekly</changefreq>
+    <priority>1.0</priority>
+  </url>
+  <url>
+    <loc>https://impeccable.style/cheatsheet</loc>
+    <lastmod>2026-04-08</lastmod>
+    <changefreq>monthly</changefreq>
+    <priority>0.7</priority>
+  </url>
+  <url>
+    <loc>https://impeccable.style/gallery</loc>
+    <lastmod>2026-04-08</lastmod>
+    <changefreq>monthly</changefreq>
+    <priority>0.7</priority>
+  </url>
+
+  <!-- Section indexes -->
+  <url>
+    <loc>https://impeccable.style/skills</loc>
+    <lastmod>2026-04-08</lastmod>
+    <changefreq>weekly</changefreq>
+    <priority>0.9</priority>
+  </url>
+  <url>
+    <loc>https://impeccable.style/anti-patterns</loc>
+    <lastmod>2026-04-08</lastmod>
+    <changefreq>weekly</changefreq>
+    <priority>0.9</priority>
+  </url>
+  <url>
+    <loc>https://impeccable.style/tutorials</loc>
+    <lastmod>2026-04-08</lastmod>
+    <changefreq>weekly</changefreq>
+    <priority>0.9</priority>
+  </url>
+
+  <!-- Skill detail pages -->
+  <url><loc>https://impeccable.style/skills/adapt</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/animate</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/arrange</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/audit</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/bolder</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/clarify</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/colorize</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/critique</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/delight</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/distill</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/extract</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/harden</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/impeccable</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/normalize</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/onboard</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/optimize</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/overdrive</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/polish</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/quieter</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/shape</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/skills/typeset</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+
+  <!-- Tutorial detail pages -->
+  <url><loc>https://impeccable.style/tutorials/getting-started</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+  <url><loc>https://impeccable.style/tutorials/critique-with-overlay</loc><lastmod>2026-04-08</lastmod><priority>0.8</priority></url>
+</urlset>

scripts/build-browser-detector.js 🔗

@@ -0,0 +1,47 @@
+#!/usr/bin/env node
+
+/**
+ * Generates src/detect-antipatterns-browser.js
+ * by stripping Node-specific sections from the universal source and wrapping in an IIFE.
+ *
+ * Run: node scripts/build-browser-detector.js
+ */
+
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, '..');
+
+const SOURCE = path.join(ROOT, 'src/detect-antipatterns.mjs');
+const OUTPUT = path.join(ROOT, 'src/detect-antipatterns-browser.js');
+
+let code = fs.readFileSync(SOURCE, 'utf-8');
+
+// Strip shebang
+code = code.replace(/^#!.*\n/, '');
+// Strip sections between @browser-strip-start / @browser-strip-end markers
+code = code.replace(/^\/\/ @browser-strip-start\n[\s\S]*?^\/\/ @browser-strip-end\n?/gm, '');
+// Set IS_BROWSER = true (dead-code eliminates Node paths)
+code = code.replace(/^const IS_BROWSER = .*$/m, 'const IS_BROWSER = true;');
+
+const output = `/**
+ * Anti-Pattern Browser Detector for Impeccable
+ * Copyright (c) 2026 Paul Bakaus
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * GENERATED -- do not edit. Source: detect-antipatterns.mjs
+ * Rebuild: node scripts/build-browser-detector.js
+ *
+ * Usage: <script src="detect-antipatterns-browser.js"></script>
+ * Re-scan: window.impeccableScan()
+ */
+(function () {
+if (typeof window === 'undefined') return;
+${code}
+})();
+`;
+
+fs.writeFileSync(OUTPUT, output);
+console.log(`Generated ${path.relative(ROOT, OUTPUT)} (${(output.length / 1024).toFixed(1)} KB)`);

scripts/build-extension.js 🔗

@@ -0,0 +1,85 @@
+#!/usr/bin/env node
+
+/**
+ * Builds the Chrome DevTools extension.
+ *
+ * 1. Generates the extension variant of the browser detector
+ * 2. Extracts antipatterns.json for the panel UI
+ * 3. Packages as extension.zip for Chrome Web Store upload
+ *
+ * Run: node scripts/build-extension.js
+ */
+
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, '..');
+const EXT_DIR = path.join(ROOT, 'extension');
+
+const SOURCE = path.join(ROOT, 'src/detect-antipatterns.mjs');
+const DETECTOR_OUTPUT = path.join(EXT_DIR, 'detector/detect.js');
+const AP_OUTPUT = path.join(EXT_DIR, 'detector/antipatterns.json');
+
+let code = fs.readFileSync(SOURCE, 'utf-8');
+
+// --- 1. Build detector ---
+
+// Strip shebang
+code = code.replace(/^#!.*\n/, '');
+// Strip sections between @browser-strip-start / @browser-strip-end markers
+code = code.replace(/^\/\/ @browser-strip-start\n[\s\S]*?^\/\/ @browser-strip-end\n?/gm, '');
+// Set IS_BROWSER = true (dead-code eliminates Node paths)
+code = code.replace(/^const IS_BROWSER = .*$/m, 'const IS_BROWSER = true;');
+
+const output = `/**
+ * Anti-Pattern Browser Detector for Impeccable (Extension Variant)
+ * Copyright (c) 2026 Paul Bakaus
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * GENERATED -- do not edit. Source: detect-antipatterns.mjs
+ * Rebuild: node scripts/build-extension.js
+ */
+(function () {
+if (typeof window === 'undefined') return;
+${code}
+})();
+`;
+
+fs.mkdirSync(path.dirname(DETECTOR_OUTPUT), { recursive: true });
+fs.writeFileSync(DETECTOR_OUTPUT, output);
+console.log(`Generated ${path.relative(ROOT, DETECTOR_OUTPUT)} (${(output.length / 1024).toFixed(1)} KB)`);
+
+// --- 2. Extract antipatterns.json ---
+
+const rawSource = fs.readFileSync(SOURCE, 'utf-8');
+const apMatch = rawSource.match(/const ANTIPATTERNS = \[([\s\S]*?)\n\];/);
+if (apMatch) {
+  // Convert JS object literals to JSON. Include description so the
+  // devtools panel can show the full rule explanation in tooltips —
+  // previously this dropped description and the panel had nothing to display.
+  const antipatterns = new Function(`return [${apMatch[1]}]`)();
+  const apJson = antipatterns.map(({ id, name, category, description }) => ({
+    id,
+    name,
+    category: category || 'quality',
+    description: description || '',
+  }));
+  fs.writeFileSync(AP_OUTPUT, JSON.stringify(apJson, null, 2) + '\n');
+  console.log(`Generated ${path.relative(ROOT, AP_OUTPUT)} (${antipatterns.length} rules)`);
+}
+
+// --- 3. Zip packaging ---
+
+import { execSync } from 'child_process';
+
+const zipPath = path.join(ROOT, 'dist/extension.zip');
+fs.mkdirSync(path.join(ROOT, 'dist'), { recursive: true });
+try { fs.unlinkSync(zipPath); } catch {}
+execSync(
+  `zip -r ${JSON.stringify(zipPath)} . -x "STORE_LISTING.md" ".DS_Store"`,
+  { cwd: EXT_DIR, stdio: 'pipe' },
+);
+const size = fs.statSync(zipPath).size;
+console.log(`Packaged ${path.relative(ROOT, zipPath)} (${(size / 1024).toFixed(1)} KB)`);

scripts/build-sub-pages.js 🔗

@@ -0,0 +1,735 @@
+/**
+ * Generate static HTML files for /skills, /anti-patterns, /tutorials.
+ *
+ * Called from both scripts/build.js (before buildStaticSite) and
+ * server/index.js (at module load), so dev and prod share the same
+ * code path and output shape.
+ *
+ * Output lives under public/skills/, public/anti-patterns/,
+ * public/tutorials/, all gitignored. Bun's HTML loader picks them up
+ * the same way it picks up the hand-authored pages.
+ */
+
+import fs from 'node:fs';
+import path from 'node:path';
+import {
+  buildSubPageData,
+  CATEGORY_ORDER,
+  CATEGORY_LABELS,
+  CATEGORY_DESCRIPTIONS,
+  LAYER_LABELS,
+  LAYER_DESCRIPTIONS,
+  GALLERY_ITEMS,
+} from './lib/sub-pages-data.js';
+import { renderMarkdown, slugify } from './lib/render-markdown.js';
+import { renderPage } from './lib/render-page.js';
+
+function escapeHtml(str) {
+  return String(str || '')
+    .replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/"/g, '&quot;')
+    .replace(/'/g, '&#39;');
+}
+
+/**
+ * Render the before/after split-compare demo block for a skill.
+ * Returns '' when the skill has no demo data (e.g. /shape).
+ */
+function renderSkillDemo(skill) {
+  if (!skill.demo) return '';
+  const { before, after, caption } = skill.demo;
+  return `
+<section class="skill-demo" aria-label="Before and after demo">
+  <div class="split-comparison" data-demo="skill-${skill.id}">
+    <p class="skill-demo-eyebrow">Drag or hover to compare</p>
+    <div class="split-container">
+      <div class="split-before">
+        <div class="split-content">${before}</div>
+      </div>
+      <div class="split-after">
+        <div class="split-content">${after || before}</div>
+      </div>
+      <div class="split-divider"></div>
+    </div>
+    <div class="split-labels">
+      <span class="split-label-item" data-point="before">Before</span>
+      ${caption ? `<p class="skill-demo-caption">${escapeHtml(caption)}</p>` : '<span></span>'}
+      <span class="split-label-item" data-point="after">After</span>
+    </div>
+  </div>
+</section>`;
+}
+
+/**
+ * Render one skill detail page HTML body (without the site shell).
+ */
+function renderSkillDetail(skill, knownSkillIds) {
+  const bodyHtml = renderMarkdown(skill.body, {
+    knownSkillIds,
+    currentSkillId: skill.id,
+  });
+
+  const editorialHtml = skill.editorial
+    ? renderMarkdown(skill.editorial.body, { knownSkillIds, currentSkillId: skill.id })
+    : '';
+
+  const demoHtml = renderSkillDemo(skill);
+
+  const tagline = skill.editorial?.frontmatter?.tagline || skill.description;
+  const categoryLabel = CATEGORY_LABELS[skill.category] || skill.category;
+
+  // Reference files as collapsible <details> blocks
+  let referencesHtml = '';
+  if (skill.references && skill.references.length > 0) {
+    const refs = skill.references
+      .map((ref) => {
+        const slug = slugify(ref.name);
+        const refBody = renderMarkdown(ref.content, {
+          knownSkillIds,
+          currentSkillId: skill.id,
+        });
+        const title = ref.name
+          .split('-')
+          .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
+          .join(' ');
+        return `
+<details class="skill-reference" id="reference-${slug}">
+  <summary><span class="skill-reference-label">Reference</span><span class="skill-reference-title">${escapeHtml(title)}</span></summary>
+  <div class="prose skill-reference-body">
+${refBody}
+  </div>
+</details>`;
+      })
+      .join('\n');
+    referencesHtml = `
+<section class="skill-references" aria-label="Reference material">
+  <h2 class="skill-references-heading">Deeper reference</h2>
+  ${refs}
+</section>`;
+  }
+
+  const metaStrip = `
+<div class="skill-meta-strip">
+  <span class="skill-meta-chip skill-meta-category" data-category="${skill.category}">${escapeHtml(categoryLabel)}</span>
+  <span class="skill-meta-chip">User-invocable</span>
+  ${skill.argumentHint ? `<span class="skill-meta-chip skill-meta-args">${escapeHtml(skill.argumentHint)}</span>` : ''}
+</div>`;
+
+  const hasDemo = demoHtml.trim().length > 0;
+
+  return `
+<article class="skill-detail">
+  <div class="skill-detail-hero${hasDemo ? ' skill-detail-hero--has-demo' : ''}">
+    <header class="skill-detail-header">
+      <p class="skill-detail-eyebrow"><a href="/skills">Skills</a> / ${escapeHtml(categoryLabel)}</p>
+      <h1 class="skill-detail-title"><span class="skill-detail-title-slash">/</span>${escapeHtml(skill.id)}</h1>
+      <p class="skill-detail-tagline">${escapeHtml(tagline)}</p>
+      ${metaStrip}
+    </header>
+    ${demoHtml}
+  </div>
+
+  ${editorialHtml ? `<section class="skill-detail-editorial prose">\n${editorialHtml}\n</section>` : ''}
+
+  <section class="skill-source-card">
+    <header class="skill-source-card-header">
+      <span class="skill-source-card-label">SKILL.md</span>
+      <span class="skill-source-card-subtitle">The canonical skill definition your AI harness loads.</span>
+    </header>
+    <div class="skill-source-card-body prose">
+${bodyHtml}
+    </div>
+  </section>
+
+  ${referencesHtml}
+</article>
+`;
+}
+
+/**
+ * Render the unified Docs sidebar used across /skills and /tutorials.
+ * Shows every skill grouped by category, then tutorials as a final
+ * group. Pass the current page identifier so we can mark it:
+ *
+ *   { kind: 'skill', id: 'polish' }
+ *   { kind: 'tutorial', slug: 'getting-started' }
+ *   null (no current page)
+ */
+function renderDocsSidebar(skillsByCategory, tutorials, current = null) {
+  // Label the toggle button with the current page so mobile users know
+  // where they are at a glance, then open the menu to switch.
+  let currentLabel = 'Docs menu';
+  if (current?.kind === 'skill') {
+    currentLabel = `/${current.id}`;
+  } else if (current?.kind === 'tutorial') {
+    const t = tutorials.find((x) => x.slug === current.slug);
+    if (t) currentLabel = t.title;
+  }
+
+  let html = `
+<aside class="skills-sidebar" aria-label="Documentation">
+  <button class="skills-sidebar-toggle" type="button" aria-expanded="false" aria-controls="skills-sidebar-inner">
+    <span class="skills-sidebar-toggle-label">${escapeHtml(currentLabel)}</span>
+    <svg class="skills-sidebar-toggle-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
+  </button>
+  <div class="skills-sidebar-inner" id="skills-sidebar-inner">
+    <p class="skills-sidebar-label">Docs</p>
+`;
+
+  // Tutorials first: walk-throughs are the on-ramp, they go at the top.
+  if (tutorials && tutorials.length > 0) {
+    html += `
+    <div class="skills-sidebar-group" data-category="tutorials">
+      <p class="skills-sidebar-group-title">Tutorials</p>
+      <ul class="skills-sidebar-list">
+${tutorials
+  .map((t) => {
+    const isCurrent = current?.kind === 'tutorial' && current.slug === t.slug;
+    const attr = isCurrent ? ' aria-current="page"' : '';
+    return `        <li><a href="/tutorials/${t.slug}"${attr}>${escapeHtml(t.title)}</a></li>`;
+  })
+  .join('\n')}
+      </ul>
+    </div>
+    <hr class="skills-sidebar-divider">
+`;
+  }
+
+  // Sub-command links that appear as indented entries after their parent skill.
+  const SUB_COMMANDS = {
+    impeccable: [
+      { id: 'impeccable-craft', label: '/impeccable craft', href: '/skills/impeccable#craft' },
+      { id: 'impeccable-teach', label: '/impeccable teach', href: '/skills/impeccable#teach' },
+      { id: 'impeccable-extract', label: '/impeccable extract', href: '/skills/impeccable#extract' },
+    ],
+  };
+
+  // Then the skills, grouped by category.
+  for (const category of CATEGORY_ORDER) {
+    const list = skillsByCategory[category] || [];
+    if (list.length === 0) continue;
+    html += `
+    <div class="skills-sidebar-group" data-category="${category}">
+      <p class="skills-sidebar-group-title">${escapeHtml(CATEGORY_LABELS[category])}</p>
+      <ul class="skills-sidebar-list">
+${list
+  .flatMap((s) => {
+    const isCurrent = current?.kind === 'skill' && current.id === s.id;
+    const attr = isCurrent ? ' aria-current="page"' : '';
+    const items = [`        <li><a href="/skills/${s.id}"${attr}>/${escapeHtml(s.id)}</a></li>`];
+    const subs = SUB_COMMANDS[s.id];
+    if (subs) {
+      for (const sub of subs) {
+        items.push(`        <li class="skills-sidebar-sub"><a href="${sub.href}">${escapeHtml(sub.label)}</a></li>`);
+      }
+    }
+    return items;
+  })
+  .join('\n')}
+      </ul>
+    </div>
+`;
+  }
+
+  html += `
+  </div>
+</aside>`;
+  return html;
+}
+
+/**
+ * Render the /skills overview main column content (not the sidebar).
+ * This is the orientation piece: what skills are, how to pick one,
+ * the six categories explained with inline cross-links to detail pages.
+ */
+function renderSkillsOverviewMain(skillsByCategory) {
+  const totalSkills = Object.values(skillsByCategory).reduce(
+    (sum, list) => sum + list.length,
+    0,
+  );
+
+  let categoriesHtml = '';
+  for (const category of CATEGORY_ORDER) {
+    const list = skillsByCategory[category] || [];
+    if (list.length === 0) continue;
+
+    const skillChips = list
+      .map(
+        (s) =>
+          `<a class="skills-overview-chip" href="/skills/${s.id}">/${escapeHtml(s.id)}</a>`,
+      )
+      .join('');
+
+    categoriesHtml += `
+    <section class="skills-overview-category" data-category="${category}" id="category-${category}">
+      <div class="skills-overview-category-meta">
+        <h2 class="skills-overview-category-title">${escapeHtml(CATEGORY_LABELS[category])}</h2>
+        <p class="skills-overview-category-count">${list.length} ${list.length === 1 ? 'skill' : 'skills'}</p>
+      </div>
+      <p class="skills-overview-category-desc">${escapeHtml(CATEGORY_DESCRIPTIONS[category])}</p>
+      <div class="skills-overview-chips">
+${skillChips}
+      </div>
+    </section>
+`;
+  }
+
+  return `
+<div class="skills-overview-content">
+  <header class="skills-overview-header">
+    <p class="sub-page-eyebrow">${totalSkills} commands</p>
+    <h1 class="sub-page-title">Skills</h1>
+    <p class="sub-page-lede">One skill, <a href="/skills/impeccable">/impeccable</a>, teaches your AI design. Eighteen commands steer the result. Each command does one job with an opinion about what good looks like.</p>
+  </header>
+
+  <section class="skills-overview-howto">
+    <h2 class="skills-overview-howto-title">How to pick one</h2>
+    <p>Skills are named after the intent you bring to them. Reviewing something? <a href="/skills/critique">/critique</a> or <a href="/skills/audit">/audit</a>. Fixing type? <a href="/skills/typeset">/typeset</a>. Last-mile pass before shipping? <a href="/skills/polish">/polish</a>. The categories below group skills by the job.</p>
+  </section>
+
+  <div class="skills-overview-categories">
+${categoriesHtml}
+  </div>
+</div>`;
+}
+
+/**
+ * Wrap sidebar + main content in the docs-browser layout shell.
+ */
+function wrapInDocsLayout(sidebarHtml, mainHtml) {
+  return `
+<div class="skills-layout">
+  ${sidebarHtml}
+  <div class="skills-main">
+${mainHtml}
+  </div>
+</div>`;
+}
+
+/**
+ * Group anti-pattern rules by skill section.
+ * Rules without a skillSection fall into a 'General quality' bucket.
+ */
+function groupRulesBySection(rules) {
+  // Canonical ordering. Additional sections referenced by rules (e.g.
+  // 'Interaction', 'Responsive' from LLM-only entries) are appended to
+  // the end, before 'General quality', so every rule renders.
+  const primaryOrder = [
+    'Visual Details',
+    'Typography',
+    'Color & Contrast',
+    'Layout & Space',
+    'Motion',
+    'Interaction',
+    'Responsive',
+  ];
+  const bySection = {};
+  for (const name of primaryOrder) bySection[name] = [];
+  bySection['General quality'] = [];
+
+  for (const rule of rules) {
+    const section = rule.skillSection || 'General quality';
+    if (!bySection[section]) bySection[section] = [];
+    bySection[section].push(rule);
+  }
+
+  // Sort each bucket: slop first (they're the named tells), then quality.
+  for (const name of Object.keys(bySection)) {
+    bySection[name].sort((a, b) => {
+      if (a.category !== b.category) return a.category === 'slop' ? -1 : 1;
+      return a.name.localeCompare(b.name);
+    });
+  }
+
+  // Final render order: primary sections first, then any extras that
+  // rules introduced, then General quality last.
+  const order = [...primaryOrder];
+  for (const name of Object.keys(bySection)) {
+    if (!order.includes(name) && name !== 'General quality') {
+      order.push(name);
+    }
+  }
+  order.push('General quality');
+
+  return { order, bySection };
+}
+
+/**
+ * Render the anti-patterns sidebar: a table of contents of rule sections
+ * with per-section rule counts. Every entry anchor-jumps to the section
+ * in the main column.
+ */
+function renderAntiPatternsSidebar(grouped) {
+  const entries = grouped.order
+    .filter((section) => grouped.bySection[section]?.length > 0)
+    .map((section) => {
+      const slug = slugify(section);
+      const count = grouped.bySection[section].length;
+      return `        <li><a href="#section-${slug}"><span>${escapeHtml(section)}</span><span class="anti-patterns-sidebar-count">${count}</span></a></li>`;
+    })
+    .join('\n');
+
+  return `
+<aside class="skills-sidebar anti-patterns-sidebar" aria-label="Anti-pattern sections">
+  <button class="skills-sidebar-toggle" type="button" aria-expanded="false" aria-controls="anti-patterns-sidebar-inner">
+    <span class="skills-sidebar-toggle-label">Sections</span>
+    <svg class="skills-sidebar-toggle-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
+  </button>
+  <div class="skills-sidebar-inner" id="anti-patterns-sidebar-inner">
+    <p class="skills-sidebar-label">Sections</p>
+    <div class="skills-sidebar-group">
+      <ul class="skills-sidebar-list anti-patterns-sidebar-list">
+${entries}
+      </ul>
+    </div>
+  </div>
+</aside>`;
+}
+
+/**
+ * Render one rule card inside the anti-patterns main column.
+ */
+function renderRuleCard(rule) {
+  const categoryLabel = rule.category === 'slop' ? 'AI slop' : 'Quality';
+  const layer = rule.layer || 'cli';
+  const layerLabel = LAYER_LABELS[layer] || layer;
+  const layerTitle = LAYER_DESCRIPTIONS[layer] || '';
+  const skillLink = rule.skillSection
+    ? `<a class="rule-card-skill-link" href="/skills/impeccable#${slugify(rule.skillSection)}">See in /impeccable</a>`
+    : '';
+  const visual = rule.visual
+    ? `<div class="rule-card-visual" aria-hidden="true"><div class="rule-card-visual-inner">${rule.visual}</div></div>`
+    : '';
+  return `
+    <article class="rule-card" id="rule-${rule.id}" data-layer="${layer}">
+      ${visual}
+      <div class="rule-card-body">
+        <div class="rule-card-head">
+          <span class="rule-card-category" data-category="${rule.category}">${categoryLabel}</span>
+          <span class="rule-card-layer" data-layer="${layer}" title="${escapeAttr(layerTitle)}">${escapeHtml(layerLabel)}</span>
+        </div>
+        <h3 class="rule-card-name">${escapeHtml(rule.name)}</h3>
+        <p class="rule-card-desc">${escapeHtml(rule.description)}</p>
+        ${skillLink}
+      </div>
+    </article>`;
+}
+
+function escapeAttr(str) {
+  return String(str || '').replace(/"/g, '&quot;');
+}
+
+/**
+ * Render the /tutorials index main content.
+ */
+function renderTutorialsIndexMain(tutorials) {
+  const cards = tutorials
+    .map(
+      (t) => `
+    <a class="tutorial-card" href="/tutorials/${t.slug}">
+      <span class="tutorial-card-number">${String(t.order).padStart(2, '0')}</span>
+      <div class="tutorial-card-body">
+        <h2 class="tutorial-card-title">${escapeHtml(t.title)}</h2>
+        <p class="tutorial-card-tagline">${escapeHtml(t.tagline || t.description)}</p>
+      </div>
+      <span class="tutorial-card-arrow">→</span>
+    </a>`,
+    )
+    .join('\n');
+
+  return `
+<div class="tutorials-content">
+  <header class="sub-page-header">
+    <p class="sub-page-eyebrow">${tutorials.length} walk-throughs</p>
+    <h1 class="sub-page-title">Tutorials</h1>
+    <p class="sub-page-lede">Short, opinionated walk-throughs of the highest-leverage workflows. Each one takes around ten minutes and ends with something working in your project.</p>
+  </header>
+
+  <div class="tutorial-cards">
+${cards}
+  </div>
+</div>`;
+}
+
+/**
+ * Render the /visual-mode page main content.
+ *
+ * Single-column layout, no sidebar. Editorial header, live iframe embed
+ * of the detector running on a synthetic slop page, three-card section
+ * explaining the invocation methods, then a grid of real specimens the
+ * user can click into to see the overlay on a different page.
+ */
+function renderVisualModeMain() {
+  const specimenCards = GALLERY_ITEMS.map(
+    (item) => `
+      <a class="gallery-card" href="/antipattern-examples/${item.id}.html">
+        <div class="gallery-card-thumb">
+          <img src="../antipattern-images/${item.id}.png" alt="${escapeAttr(item.title)} specimen" loading="lazy" width="540" height="540">
+        </div>
+        <div class="gallery-card-body">
+          <h3 class="gallery-card-title">${escapeHtml(item.title)}</h3>
+          <p class="gallery-card-desc">${escapeHtml(item.desc)}</p>
+        </div>
+      </a>`,
+  ).join('\n');
+
+  return `
+<div class="visual-mode-page">
+  <header class="visual-mode-page-header">
+    <p class="sub-page-eyebrow">Live detection overlay</p>
+    <h1 class="sub-page-title">Visual Mode</h1>
+    <p class="sub-page-lede">See every anti-pattern flagged directly on the page. No screenshots, no JSON to map back to line numbers. The overlay draws an outline and a label on every element the detector catches, so you fix them in place.</p>
+  </header>
+
+  <section class="visual-mode-demo-wrap" aria-label="Visual Mode demo">
+    <div class="visual-mode-preview">
+      <div class="visual-mode-preview-header">
+        <span class="visual-mode-preview-dot red"></span>
+        <span class="visual-mode-preview-dot yellow"></span>
+        <span class="visual-mode-preview-dot green"></span>
+        <span class="visual-mode-preview-title">Live on a synthetic slop page</span>
+      </div>
+      <iframe src="/antipattern-examples/visual-mode-demo.html" class="visual-mode-frame" loading="lazy" title="Impeccable overlay running on a demo page"></iframe>
+    </div>
+    <p class="visual-mode-demo-caption">Hover or tap any outlined element to see which rule fired.</p>
+  </section>
+
+  <section class="visual-mode-methods" aria-label="Where to run Visual Mode">
+    <h2 class="visual-mode-methods-title">Three ways to run it</h2>
+    <div class="visual-mode-methods-grid">
+      <article class="visual-mode-method">
+        <p class="visual-mode-method-label">Inside /critique</p>
+        <h3 class="visual-mode-method-name"><a href="/skills/critique">/critique</a></h3>
+        <p class="visual-mode-method-desc">The design review skill opens the overlay automatically during its browser assessment pass. You get the deterministic findings highlighted in place while the LLM runs its separate heuristic review.</p>
+      </article>
+      <article class="visual-mode-method">
+        <p class="visual-mode-method-label">Standalone CLI</p>
+        <h3 class="visual-mode-method-name"><code>npx impeccable live</code></h3>
+        <p class="visual-mode-method-desc">Starts a local server that serves the detector script. Inject it into any page via a <code>&lt;script&gt;</code> tag to see the overlay. Works on your own dev server, a staging URL, or anyone's live page.</p>
+      </article>
+      <article class="visual-mode-method">
+        <p class="visual-mode-method-label">Easiest</p>
+        <h3 class="visual-mode-method-name">Chrome extension</h3>
+        <p class="visual-mode-method-desc">One-click activation on any tab. <a href="https://chromewebstore.google.com/detail/impeccable/bdkgmiklpdmaojlpflclinlofgjfpabf" target="_blank" rel="noopener">Install from Chrome Web Store &rarr;</a></p>
+      </article>
+    </div>
+  </section>
+
+  <section class="visual-mode-gallery" id="try-it-live" aria-label="Try it on synthetic specimens">
+    <header class="visual-mode-gallery-header">
+      <h2 class="visual-mode-gallery-title">Try it live</h2>
+      <p class="visual-mode-gallery-lede">These ${GALLERY_ITEMS.length} synthetic slop pages ship with the detector script baked in. Click any to see the overlay running on a real page, then scroll around and hover the outlined elements.</p>
+    </header>
+    <div class="gallery-grid">
+${specimenCards}
+    </div>
+  </section>
+</div>`;
+}
+
+/**
+ * Render a tutorial detail page main content.
+ */
+function renderTutorialDetail(tutorial, knownSkillIds) {
+  const bodyHtml = renderMarkdown(tutorial.body, { knownSkillIds });
+  return `
+<article class="tutorial-detail">
+  <header class="tutorial-detail-header">
+    <p class="skill-detail-eyebrow"><a href="/tutorials">Tutorials</a> / ${String(tutorial.order).padStart(2, '0')}</p>
+    <h1 class="tutorial-detail-title">${escapeHtml(tutorial.title)}</h1>
+    ${tutorial.tagline ? `<p class="tutorial-detail-tagline">${escapeHtml(tutorial.tagline)}</p>` : ''}
+  </header>
+
+  <section class="tutorial-detail-body prose">
+${bodyHtml}
+  </section>
+</article>`;
+}
+
+/**
+ * Render the /anti-patterns main column content.
+ */
+function renderAntiPatternsMain(grouped, totalRules) {
+  let sectionsHtml = '';
+  for (const section of grouped.order) {
+    const rules = grouped.bySection[section] || [];
+    if (rules.length === 0) continue;
+    const slug = slugify(section);
+    sectionsHtml += `
+    <section class="anti-patterns-section" id="section-${slug}">
+      <header class="anti-patterns-section-header">
+        <h2 class="anti-patterns-section-title">${escapeHtml(section)}</h2>
+        <p class="anti-patterns-section-count">${rules.length} ${rules.length === 1 ? 'rule' : 'rules'}</p>
+      </header>
+      <div class="rule-card-grid">
+${rules.map(renderRuleCard).join('\n')}
+      </div>
+    </section>`;
+  }
+
+  const detectedCount = grouped.order
+    .flatMap((s) => grouped.bySection[s] || [])
+    .filter((r) => r.layer !== 'llm').length;
+  const llmCount = totalRules - detectedCount;
+
+  return `
+<div class="anti-patterns-content">
+  <header class="anti-patterns-header">
+    <p class="sub-page-eyebrow">${totalRules} rules</p>
+    <h1 class="sub-page-title">Anti-patterns</h1>
+    <p class="sub-page-lede">The full catalog of patterns <a href="/skills/impeccable">/impeccable</a> teaches against. ${detectedCount} are caught by a deterministic detector (<code>npx impeccable detect</code> or the browser extension). ${llmCount} can only be flagged by <a href="/skills/critique">/critique</a>'s LLM review pass. Want to see them live on real pages? Try <a href="/visual-mode">Visual Mode</a>.</p>
+  </header>
+
+  <details class="anti-patterns-legend">
+    <summary class="anti-patterns-legend-summary">
+      <span class="anti-patterns-legend-title">How to read this</span>
+      <svg class="anti-patterns-legend-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
+    </summary>
+    <div class="anti-patterns-legend-body">
+      <p><strong>AI slop</strong> rules flag the visible tells of AI-generated UIs. <strong>Quality</strong> rules flag general design mistakes that are not AI-specific but still hurt the work. Each rule also shows how it is detected:</p>
+      <dl class="anti-patterns-legend-layers">
+        <div><dt><span class="rule-card-layer" data-layer="cli">CLI</span></dt><dd>Deterministic. Runs from <code>npx impeccable detect</code> on files, no browser required.</dd></div>
+        <div><dt><span class="rule-card-layer" data-layer="browser">Browser</span></dt><dd>Deterministic, but needs real browser layout. Runs via the browser extension or Puppeteer, not the plain CLI.</dd></div>
+        <div><dt><span class="rule-card-layer" data-layer="llm">LLM only</span></dt><dd>No deterministic detector. Caught by <a href="/skills/critique">/critique</a> during its LLM design review.</dd></div>
+      </dl>
+    </div>
+  </details>
+
+  <div class="anti-patterns-sections">
+${sectionsHtml}
+  </div>
+</div>`;
+}
+
+/**
+ * Entry point. Generates all sub-page HTML files.
+ *
+ * @param {string} rootDir
+ * @returns {Promise<{ files: string[] }>} list of generated file paths (absolute)
+ */
+export async function generateSubPages(rootDir) {
+  const data = await buildSubPageData(rootDir);
+  const outDirs = {
+    skills: path.join(rootDir, 'public/skills'),
+    antiPatterns: path.join(rootDir, 'public/anti-patterns'),
+    tutorials: path.join(rootDir, 'public/tutorials'),
+    visualMode: path.join(rootDir, 'public/visual-mode'),
+  };
+
+  // Fresh output dirs each time so stale files don't linger.
+  for (const dir of Object.values(outDirs)) {
+    if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true });
+    fs.mkdirSync(dir, { recursive: true });
+  }
+
+  const generated = [];
+
+  // Skills index: docs-browser layout with unified sidebar.
+  {
+    const sidebar = renderDocsSidebar(data.skillsByCategory, data.tutorials, null);
+    const main = renderSkillsOverviewMain(data.skillsByCategory);
+    const html = renderPage({
+      title: 'Skills | Impeccable',
+      description:
+        '18 commands that teach your AI harness how to design. Browse by category: create, evaluate, refine, simplify, harden.',
+      bodyHtml: wrapInDocsLayout(sidebar, main),
+      activeNav: 'docs',
+      canonicalPath: '/skills',
+      bodyClass: 'sub-page skills-layout-page',
+    });
+    const out = path.join(outDirs.skills, 'index.html');
+    fs.writeFileSync(out, html, 'utf-8');
+    generated.push(out);
+  }
+
+  // Skills detail pages: same docs-browser shell as the overview.
+  for (const skill of data.skills) {
+    const sidebar = renderDocsSidebar(data.skillsByCategory, data.tutorials, { kind: 'skill', id: skill.id });
+    const main = renderSkillDetail(skill, data.knownSkillIds);
+    const title = `/${skill.id} | Impeccable`;
+    const description = skill.editorial?.frontmatter?.tagline || skill.description;
+    const html = renderPage({
+      title,
+      description,
+      bodyHtml: wrapInDocsLayout(sidebar, main),
+      activeNav: 'docs',
+      canonicalPath: `/skills/${skill.id}`,
+      bodyClass: 'sub-page skills-layout-page',
+    });
+    const out = path.join(outDirs.skills, `${skill.id}.html`);
+    fs.writeFileSync(out, html, 'utf-8');
+    generated.push(out);
+  }
+
+  // Anti-patterns index: single page, docs-browser shell with TOC sidebar.
+  {
+    const grouped = groupRulesBySection(data.rules);
+    const sidebar = renderAntiPatternsSidebar(grouped);
+    const main = renderAntiPatternsMain(grouped, data.rules.length);
+    const html = renderPage({
+      title: 'Anti-patterns | Impeccable',
+      description: `${data.rules.length} deterministic detection rules that flag the visible tells of AI-generated interfaces and common quality issues. Used by npx impeccable detect and the browser extension.`,
+      bodyHtml: wrapInDocsLayout(sidebar, main),
+      activeNav: 'anti-patterns',
+      canonicalPath: '/anti-patterns',
+      bodyClass: 'sub-page skills-layout-page anti-patterns-page',
+    });
+    const out = path.join(outDirs.antiPatterns, 'index.html');
+    fs.writeFileSync(out, html, 'utf-8');
+    generated.push(out);
+  }
+
+  // Tutorials index (under the unified Docs umbrella).
+  if (data.tutorials.length > 0) {
+    const sidebar = renderDocsSidebar(data.skillsByCategory, data.tutorials, null);
+    const main = renderTutorialsIndexMain(data.tutorials);
+    const html = renderPage({
+      title: 'Tutorials | Impeccable',
+      description: `${data.tutorials.length} short, opinionated walk-throughs of the highest-leverage Impeccable workflows.`,
+      bodyHtml: wrapInDocsLayout(sidebar, main),
+      activeNav: 'docs',
+      canonicalPath: '/tutorials',
+      bodyClass: 'sub-page skills-layout-page tutorials-page',
+    });
+    const out = path.join(outDirs.tutorials, 'index.html');
+    fs.writeFileSync(out, html, 'utf-8');
+    generated.push(out);
+  }
+
+  // Visual Mode: single standalone page, no sidebar, single-column layout.
+  {
+    const html = renderPage({
+      title: 'Visual Mode | Impeccable',
+      description:
+        'See every anti-pattern flagged directly on the page. Live detection overlay from Impeccable, available via /critique, npx impeccable live, or the upcoming Chrome extension.',
+      bodyHtml: renderVisualModeMain(),
+      activeNav: 'visual-mode',
+      canonicalPath: '/visual-mode',
+      bodyClass: 'sub-page visual-mode-page-body',
+    });
+    const out = path.join(outDirs.visualMode, 'index.html');
+    fs.writeFileSync(out, html, 'utf-8');
+    generated.push(out);
+  }
+
+  // Tutorial detail pages.
+  for (const tutorial of data.tutorials) {
+    const sidebar = renderDocsSidebar(data.skillsByCategory, data.tutorials, { kind: 'tutorial', slug: tutorial.slug });
+    const main = renderTutorialDetail(tutorial, data.knownSkillIds);
+    const html = renderPage({
+      title: `${tutorial.title} | Tutorials | Impeccable`,
+      description: tutorial.description || tutorial.tagline || '',
+      bodyHtml: wrapInDocsLayout(sidebar, main),
+      activeNav: 'docs',
+      canonicalPath: `/tutorials/${tutorial.slug}`,
+      bodyClass: 'sub-page skills-layout-page tutorials-page',
+    });
+    const out = path.join(outDirs.tutorials, `${tutorial.slug}.html`);
+    fs.writeFileSync(out, html, 'utf-8');
+    generated.push(out);
+  }
+
+  return { files: generated };
+}

scripts/build.js 🔗

@@ -0,0 +1,664 @@
+#!/usr/bin/env node
+
+/**
+ * Build System for Cross-Provider Design Skills
+ *
+ * Transforms source skills into provider-specific formats:
+ * - Cursor: .cursor/skills/
+ * - Claude Code: .claude/skills/
+ * - Gemini: .gemini/skills/
+ * - Codex: .codex/skills/
+ * - Agents: .agents/skills/ (VS Code Copilot + Antigravity)
+ *
+ * Also assembles a universal ZIP containing all providers,
+ * and builds Tailwind CSS for production deployment.
+ */
+
+import path from 'path';
+import fs from 'fs';
+import { fileURLToPath } from 'url';
+import { readSourceFiles, readPatterns } from './lib/utils.js';
+import { createTransformer, PROVIDERS } from './lib/transformers/index.js';
+import { createAllZips } from './lib/zip.js';
+import { generateSubPages } from './build-sub-pages.js';
+
+/**
+ * Generate authoritative counts from source data and write to public/js/generated/counts.js.
+ * Also validates that key HTML files reference the correct numbers.
+ */
+function generateCounts(rootDir, skills, buildDir) {
+  // Count active (non-deprecated) user-invocable commands
+  const activeCommands = skills.filter(s => {
+    if (!s.userInvocable) return false;
+    const content = fs.readFileSync(s.filePath, 'utf-8');
+    return !content.includes('DEPRECATED');
+  });
+  const commandCount = activeCommands.length;
+
+  // Count detection rules from impeccable package
+  const detectPkgPath = path.join(rootDir, 'src/detect-antipatterns.mjs');
+  const detectorSrc = fs.readFileSync(detectPkgPath, 'utf-8');
+  const ruleIds = new Set();
+  for (const match of detectorSrc.matchAll(/^\s+id: '([^']+)'/gm)) {
+    ruleIds.add(match[1]);
+  }
+  const detectionCount = ruleIds.size;
+
+  // Write generated counts module
+  const genDir = path.join(rootDir, 'public/js/generated');
+  fs.mkdirSync(genDir, { recursive: true });
+  fs.writeFileSync(path.join(genDir, 'counts.js'),
+    `// GENERATED by build.js — do not edit\n` +
+    `export const COMMAND_COUNT = ${commandCount};\n` +
+    `export const DETECTION_COUNT = ${detectionCount};\n`
+  );
+
+  // Validate counts in key files
+  const filesToCheck = [
+    'public/index.html',
+    'public/cheatsheet.html',
+    'README.md',
+    'NOTICE.md',
+    'AGENTS.md',
+    '.claude-plugin/plugin.json',
+    '.claude-plugin/marketplace.json',
+  ];
+
+  let errors = 0;
+  for (const relPath of filesToCheck) {
+    const absPath = path.join(rootDir, relPath);
+    if (!fs.existsSync(absPath)) continue;
+    const content = fs.readFileSync(absPath, 'utf-8');
+
+    // Check for stale command counts (look for "N commands" or "N skills" patterns)
+    // Strip changelog list content to avoid flagging historical counts
+    const strippedContent = content.replace(/<ul class="changelog-items">[\s\S]*?<\/ul>/g, '');
+    const countPattern = /\b(\d+)\s+(design\s+)?(commands|skills|steering commands)/gi;
+    for (const match of strippedContent.matchAll(countPattern)) {
+      const num = parseInt(match[1]);
+      // Allow 1 (for "1 skill") and the correct count
+      if (num !== commandCount && num !== 1) {
+        console.error(`  ❌ ${relPath}: found "${match[0]}" but active command count is ${commandCount}`);
+        errors++;
+      }
+    }
+
+    // Check for stale detection counts
+    const detectPattern = /\b(\d+)\s+(deterministic\s+)?(checks|patterns|rules|detections)/gi;
+    for (const match of content.matchAll(detectPattern)) {
+      const num = parseInt(match[1]);
+      if (num !== detectionCount && num > 10) { // ignore small numbers like "3 patterns"
+        console.error(`  ❌ ${relPath}: found "${match[0]}" but detection count is ${detectionCount}`);
+        errors++;
+      }
+    }
+  }
+
+  if (errors > 0) {
+    console.error(`\n❌ ${errors} stale count reference(s) found. Update them to match source of truth.`);
+  }
+
+  console.log(`✓ Generated counts: ${commandCount} commands, ${detectionCount} detection rules`);
+  return errors;
+}
+
+/**
+ * Cross-validate that every detection rule with a `skillGuideline` has a
+ * matching DON'T line in the right section of source/skills/impeccable/SKILL.md.
+ *
+ * This is the linchpin of the single-source-of-truth design: it catches drift
+ * between the engine's ANTIPATTERNS and the human-written DO/DON'T prose.
+ *
+ * Returns the number of validation errors. Build fails if > 0.
+ */
+function validateAntipatternRules(rootDir) {
+  const detectPath = path.join(rootDir, 'src/detect-antipatterns.mjs');
+  const src = fs.readFileSync(detectPath, 'utf-8');
+  const apMatch = src.match(/const ANTIPATTERNS = \[([\s\S]*?)\n\];/);
+  if (!apMatch) {
+    console.error('  ❌ Could not extract ANTIPATTERNS from detect-antipatterns.mjs');
+    return 1;
+  }
+  const antipatterns = new Function(`return [${apMatch[1]}]`)();
+  const { antipatterns: skillSections } = readPatterns(rootDir);
+
+  // Build section -> joined-DON'T-text lookup for substring matching.
+  // Lowercased for case-insensitive matching: my XML refactor uses sentence-
+  // case "DO NOT nest cards" while the rules' skillGuideline strings are
+  // sentence-cased "Nest cards inside cards" (a fragment from the original
+  // markdown bullet "**DON'T**: Nest cards inside cards.").
+  const sectionText = {};
+  for (const section of skillSections) {
+    sectionText[section.name] = section.items.join('\n').toLowerCase();
+  }
+
+  let errors = 0;
+  let validated = 0;
+  for (const rule of antipatterns) {
+    if (!rule.skillGuideline) continue;
+    if (!rule.skillSection) {
+      console.error(`  ❌ Rule '${rule.id}' declares skillGuideline but no skillSection`);
+      errors++;
+      continue;
+    }
+    const text = sectionText[rule.skillSection];
+    if (!text) {
+      console.error(`  ❌ Rule '${rule.id}': skillSection '${rule.skillSection}' has no DON'T lines in source/skills/impeccable/SKILL.md`);
+      errors++;
+      continue;
+    }
+    if (!text.includes(rule.skillGuideline.toLowerCase())) {
+      console.error(`  ❌ Rule '${rule.id}': skillGuideline '${rule.skillGuideline}' not found in any DON'T of section '${rule.skillSection}' in source/skills/impeccable/SKILL.md`);
+      errors++;
+      continue;
+    }
+    validated++;
+  }
+
+  if (errors > 0) {
+    console.error(`\n❌ ${errors} anti-pattern rule(s) drift between src/detect-antipatterns.mjs and source/skills/impeccable/SKILL.md`);
+  } else {
+    console.log(`✓ Validated ${validated}/${antipatterns.length} anti-pattern rules against impeccable SKILL.md`);
+  }
+  return errors;
+}
+
+/**
+ * Scan user-facing copy for em dashes (— or &mdash;).
+ * Em dashes in project copy are a known anti-pattern here; flag them loudly.
+ * Only scans files where we author copy, not vendored or generated output.
+ *
+ * Returns the number of occurrences found.
+ */
+function validateNoEmDashes(rootDir) {
+  const targets = [
+    'content/site',
+    'public/index.html',
+    'public/cheatsheet.html',
+    'public/privacy.html',
+    'scripts/build-sub-pages.js',
+    'scripts/lib/sub-pages-data.js',
+  ];
+  const extensions = new Set(['.html', '.md', '.js', '.mjs', '.css']);
+  const emDashPatterns = [/—/g, /&mdash;/gi, /&#8212;/gi, /&#x2014;/gi];
+  let errors = 0;
+
+  const scan = (absPath, rel) => {
+    const stat = fs.statSync(absPath);
+    if (stat.isDirectory()) {
+      for (const entry of fs.readdirSync(absPath)) {
+        scan(path.join(absPath, entry), path.join(rel, entry));
+      }
+      return;
+    }
+    if (!extensions.has(path.extname(absPath))) return;
+    const src = fs.readFileSync(absPath, 'utf-8');
+    const lines = src.split('\n');
+    lines.forEach((line, i) => {
+      for (const re of emDashPatterns) {
+        if (re.test(line)) {
+          console.error(`  ❌ ${rel}:${i + 1}: em dash in copy → ${line.trim().slice(0, 120)}`);
+          errors++;
+          break;
+        }
+        re.lastIndex = 0;
+      }
+    });
+  };
+
+  for (const target of targets) {
+    const full = path.join(rootDir, target);
+    if (fs.existsSync(full)) scan(full, target);
+  }
+
+  if (errors === 0) {
+    console.log(`✓ No em dashes in project copy`);
+  } else {
+    console.error(`\n❌ ${errors} em dash(es) in project copy. Use commas, colons, or parentheses.`);
+  }
+  return errors;
+}
+
+/**
+ * Validate that every hand-authored HTML page carries the shared site header.
+ * The partial is stamped with `<!-- site-header v1 -->` so drift is loud.
+ *
+ * Returns the number of validation errors. Build fails if > 0.
+ */
+function validateSiteHeader(rootDir) {
+  const pages = [
+    'public/index.html',
+    'public/cheatsheet.html',
+    'public/privacy.html',
+  ];
+  const marker = '<!-- site-header v1 -->';
+  let errors = 0;
+  for (const rel of pages) {
+    const full = path.join(rootDir, rel);
+    if (!fs.existsSync(full)) {
+      console.error(`  ❌ ${rel} is missing`);
+      errors++;
+      continue;
+    }
+    const src = fs.readFileSync(full, 'utf-8');
+    if (!src.includes(marker)) {
+      console.error(`  ❌ ${rel} is missing the shared site header marker '${marker}'`);
+      errors++;
+    }
+  }
+  if (errors === 0) {
+    console.log(`✓ Validated site header on ${pages.length} hand-authored pages`);
+  }
+  return errors;
+}
+
+/**
+ * Copy directory recursively
+ */
+function copyDirSync(src, dest) {
+  fs.mkdirSync(dest, { recursive: true });
+  const entries = fs.readdirSync(src, { withFileTypes: true });
+  for (const entry of entries) {
+    const srcPath = path.join(src, entry.name);
+    const destPath = path.join(dest, entry.name);
+    if (entry.isDirectory()) {
+      copyDirSync(srcPath, destPath);
+    } else {
+      fs.copyFileSync(srcPath, destPath);
+    }
+  }
+}
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const ROOT_DIR = path.resolve(__dirname, '..');
+const DIST_DIR = path.join(ROOT_DIR, 'dist');
+
+/**
+ * Build static site using Bun's HTML bundler
+ * Bun's HTML loader resolves <link rel="stylesheet"> and inlines CSS @imports.
+ */
+async function buildStaticSite(extraEntrypoints = []) {
+  const entrypoints = [
+    path.join(ROOT_DIR, 'public', 'index.html'),
+    path.join(ROOT_DIR, 'public', 'cheatsheet.html'),
+    path.join(ROOT_DIR, 'public', 'privacy.html'),
+    ...extraEntrypoints,
+  ];
+  const outdir = path.join(ROOT_DIR, 'build');
+
+  console.log(`📦 Building static site with Bun (${entrypoints.length} HTML entries)...`);
+
+  try {
+    const result = await Bun.build({
+      entrypoints: entrypoints,
+      outdir: outdir,
+      minify: true,
+      sourcemap: 'linked',
+      // Older Bun versions (e.g. the one Cloudflare Pages ships) don't dedupe
+      // shared CSS/JS chunks across HTML entrypoints — every entry tries to
+      // emit its own copy, and three different sub-pages all named index.html
+      // (under skills/, tutorials/, anti-patterns/) collide on the same
+      // chunk filename. Including [dir] in the chunk template scopes each
+      // chunk to its entry's directory so the names stay unique even when
+      // dedupe is off. Local Bun still emits a single shared chunk; CF Bun
+      // emits one per entry but each lands in its own directory.
+      naming: {
+        entry: '[dir]/[name].[ext]',
+        chunk: '[dir]/[name]-[hash].[ext]',
+        asset: '[dir]/[name]-[hash].[ext]',
+      },
+    });
+
+    if (!result.success) {
+      console.error('Build failed:');
+      for (const log of result.logs) {
+        console.error(log.message || log);
+        if (log.position) {
+          console.error(`  at ${log.position.file}:${log.position.line}:${log.position.column}`);
+        }
+      }
+      process.exit(1);
+    }
+
+    // Calculate total size
+    const totalSize = result.outputs.reduce((sum, o) => sum + o.size, 0);
+    const htmlFiles = result.outputs.filter(o => o.path.endsWith('.html'));
+    const jsFiles = result.outputs.filter(o => o.path.endsWith('.js'));
+    const cssFiles = result.outputs.filter(o => o.path.endsWith('.css'));
+
+    // When entrypoints span multiple depths under public/ (e.g. public/index.html
+    // + public/skills/polish.html), Bun's HTML loader preserves the full public/
+    // prefix in the output tree. Flatten build/public/* up to build/*.
+    const nestedPublic = path.join(outdir, 'public');
+    if (fs.existsSync(nestedPublic)) {
+      for (const entry of fs.readdirSync(nestedPublic, { withFileTypes: true })) {
+        const from = path.join(nestedPublic, entry.name);
+        const to = path.join(outdir, entry.name);
+        if (fs.existsSync(to)) fs.rmSync(to, { recursive: true, force: true });
+        fs.renameSync(from, to);
+      }
+      fs.rmdirSync(nestedPublic);
+    }
+
+    console.log(`✓ Static site built to ./build/`);
+    console.log(`  HTML: ${htmlFiles.length} file(s)`);
+    console.log(`  JS: ${jsFiles.length} file(s) (${(jsFiles.reduce((s, f) => s + f.size, 0) / 1024).toFixed(1)} KB)`);
+    console.log(`  CSS: ${cssFiles.length} file(s) (${(cssFiles.reduce((s, f) => s + f.size, 0) / 1024).toFixed(1)} KB)`);
+    console.log(`  Total: ${(totalSize / 1024).toFixed(1)} KB\n`);
+
+    return result;
+  } catch (error) {
+    // Bun's build aggregator errors expose details on `error.errors` (an
+    // array of resolution / parse failures), not `error.stack`. Print
+    // both so CI logs surface the real cause instead of "undefined".
+    console.error('Failed to build static site:', error.message);
+    if (error.errors?.length) {
+      for (const e of error.errors) {
+        console.error('  -', e.message || e);
+      }
+    }
+    if (error.logs?.length) {
+      for (const log of error.logs) {
+        console.error(log.message || log);
+      }
+    }
+    if (error.stack) console.error(error.stack);
+    process.exit(1);
+  }
+}
+
+/**
+ * Assemble universal directory from all provider outputs
+ */
+function assembleUniversal(distDir, suffix = '') {
+  const universalDir = path.join(distDir, `universal${suffix}`);
+
+  // Clean and recreate
+  if (fs.existsSync(universalDir)) {
+    fs.rmSync(universalDir, { recursive: true, force: true });
+  }
+
+  const providerConfigs = Object.values(PROVIDERS);
+
+  for (const { provider, configDir } of providerConfigs) {
+    const src = path.join(distDir, `${provider}${suffix}`, configDir);
+    const dest = path.join(universalDir, configDir);
+    if (fs.existsSync(src)) {
+      copyDirSync(src, dest);
+    }
+  }
+
+  // Add a visible README so macOS users don't see an empty folder
+  // (all provider dirs are dotfiles, hidden by default in Finder)
+  const prefixNote = suffix ? '\nSkills in this bundle are prefixed with i- (e.g. /i-audit) to avoid conflicts.\n' : '';
+  fs.writeFileSync(path.join(universalDir, 'README.txt'),
+`Impeccable — Design fluency for AI harnesses
+https://impeccable.style
+${prefixNote}
+This folder contains skills for all supported tools:
+
+  .cursor/    → Cursor
+  .claude/    → Claude Code
+  .gemini/    → Gemini CLI
+  .codex/     → Codex CLI
+  .agents/    → VS Code Copilot, Antigravity
+  .kiro/      → Kiro
+  .opencode/  → OpenCode
+  .pi/        → Pi
+  .trae-cn/   → Trae China
+  .trae/      → Trae International
+
+To install, copy the relevant folder(s) into your project root.
+These are hidden folders (dotfiles) — press Cmd+Shift+. in Finder to see them.
+`);
+
+  const label = suffix ? ' (prefixed)' : '';
+  console.log(`✓ Assembled universal${label} directory (${providerConfigs.length} providers)`);
+}
+
+/**
+ * Generate static API data for Cloudflare Pages deployment.
+ * Pre-builds all API responses as JSON files so they can be served
+ * as static assets via _redirects rewrites (no function invocations needed).
+ */
+function generateApiData(buildDir, skills, patterns) {
+  const apiDir = path.join(buildDir, '_data', 'api');
+  fs.mkdirSync(apiDir, { recursive: true });
+
+  // skills.json
+  const skillsData = skills.map(s => ({
+    id: path.basename(path.dirname(s.filePath)),
+    name: s.name,
+    description: s.description,
+    userInvocable: s.userInvocable,
+  }));
+  fs.writeFileSync(path.join(apiDir, 'skills.json'), JSON.stringify(skillsData));
+
+  // commands.json (user-invocable skills only)
+  const commandsData = skillsData.filter(s => s.userInvocable);
+  fs.writeFileSync(path.join(apiDir, 'commands.json'), JSON.stringify(commandsData));
+
+  // patterns.json
+  fs.writeFileSync(path.join(apiDir, 'patterns.json'), JSON.stringify(patterns));
+
+  // command-source/{id}.json (one per skill)
+  const cmdSourceDir = path.join(apiDir, 'command-source');
+  fs.mkdirSync(cmdSourceDir, { recursive: true });
+  for (const skill of skills) {
+    const id = path.basename(path.dirname(skill.filePath));
+    const content = fs.readFileSync(skill.filePath, 'utf-8');
+    fs.writeFileSync(
+      path.join(cmdSourceDir, `${id}.json`),
+      JSON.stringify({ content })
+    );
+  }
+
+  console.log(`✓ Generated static API data (${skillsData.length} skills, ${commandsData.length} commands)`);
+}
+
+/**
+ * Copy dist files to build output for Cloudflare Pages Functions access.
+ * Download functions use env.ASSETS.fetch() to read these files.
+ */
+function copyDistToBuild(distDir, buildDir) {
+  const destDir = path.join(buildDir, '_data', 'dist');
+  copyDirSync(distDir, destDir);
+  console.log('✓ Copied dist files to build output');
+}
+
+/**
+ * Generate Cloudflare Pages config files (_headers, _redirects)
+ */
+function generateCFConfig(buildDir) {
+  // _headers: security + cache headers
+  const headers = `/*
+  X-Content-Type-Options: nosniff
+  X-Frame-Options: SAMEORIGIN
+
+# HTML pages: browser always revalidates, CDN caches 1h
+/*.html
+  Cache-Control: public, max-age=0, s-maxage=3600, stale-while-revalidate=600
+
+# Hashed JS/CSS bundles: immutable (filename changes on content change)
+/assets/*.js
+  Cache-Control: public, max-age=31536000, immutable
+
+/assets/*.css
+  Cache-Control: public, max-age=31536000, immutable
+
+# Static images and logos: 1 week + 1 day stale
+/assets/*.png
+  Cache-Control: public, max-age=604800, stale-while-revalidate=86400
+
+/assets/*.svg
+  Cache-Control: public, max-age=604800, stale-while-revalidate=86400
+
+/assets/*.webp
+  Cache-Control: public, max-age=604800, stale-while-revalidate=86400
+
+/antipattern-images/*
+  Cache-Control: public, max-age=604800, stale-while-revalidate=86400
+
+# Root static assets (favicon, og-image, etc.)
+/favicon.svg
+  Cache-Control: public, max-age=604800, stale-while-revalidate=86400
+
+/og-image.jpg
+  Cache-Control: public, max-age=604800, stale-while-revalidate=86400
+
+/apple-touch-icon.png
+  Cache-Control: public, max-age=604800, stale-while-revalidate=86400
+
+# ZIP downloads: 1h cache
+/dist/*.zip
+  Cache-Control: public, max-age=3600, stale-while-revalidate=600
+
+# API routes: CDN caches 24h
+/api/*
+  Cache-Control: public, s-maxage=86400, stale-while-revalidate=3600
+
+/_data/api/*
+  Cache-Control: public, s-maxage=86400, stale-while-revalidate=3600
+`;
+  fs.writeFileSync(path.join(buildDir, '_headers'), headers);
+
+  // _redirects: rewrite JSON API routes to static files (200 = rewrite, not redirect)
+  const redirects = `/api/skills /_data/api/skills.json 200
+/api/commands /_data/api/commands.json 200
+/api/patterns /_data/api/patterns.json 200
+/api/command-source/:id /_data/api/command-source/:id.json 200
+/gallery /visual-mode#try-it-live 301
+`;
+  fs.writeFileSync(path.join(buildDir, '_redirects'), redirects);
+
+  // _routes.json: tell Cloudflare Pages which paths invoke Functions
+  // Without this, the SPA fallback serves index.html for function routes
+  const routes = {
+    version: 1,
+    include: ['/api/download/*'],
+    exclude: [],
+  };
+  fs.writeFileSync(path.join(buildDir, '_routes.json'), JSON.stringify(routes, null, 2));
+
+  console.log('✓ Generated Cloudflare Pages config (_headers, _redirects, _routes.json)');
+}
+
+/**
+ * Main build process
+ */
+async function build() {
+  console.log('🔨 Building cross-provider design skills...\n');
+
+  // Pre-generate sub-pages (skills, anti-patterns, tutorials) from source
+  console.log('📝 Generating sub-pages...');
+  const { files: subPageFiles } = await generateSubPages(ROOT_DIR);
+  console.log(`✓ Generated ${subPageFiles.length} sub-page(s)\n`);
+
+  // Bundle HTML, JS, and CSS with Bun (including generated sub-pages)
+  await buildStaticSite(subPageFiles);
+
+  // Copy root-level static assets that need stable (unhashed) URLs
+  const staticAssets = ['og-image.jpg', 'robots.txt', 'sitemap.xml', 'favicon.svg', 'apple-touch-icon.png'];
+  const buildDir = path.join(ROOT_DIR, 'build');
+  for (const asset of staticAssets) {
+    const src = path.join(ROOT_DIR, 'public', asset);
+    if (fs.existsSync(src)) {
+      fs.copyFileSync(src, path.join(buildDir, asset));
+    }
+  }
+
+  // Copy antipattern examples (self-contained HTML, not Bun entrypoints)
+  const examplesDir = path.join(ROOT_DIR, 'public', 'antipattern-examples');
+  if (fs.existsSync(examplesDir)) {
+    copyDirSync(examplesDir, path.join(buildDir, 'antipattern-examples'));
+  }
+
+  // Copy browser detector script (referenced by antipattern examples at /js/...)
+  const detectorSrc = path.join(ROOT_DIR, 'src', 'detect-antipatterns-browser.js');
+  if (fs.existsSync(detectorSrc)) {
+    const jsDir = path.join(buildDir, 'js');
+    fs.mkdirSync(jsDir, { recursive: true });
+    fs.copyFileSync(detectorSrc, path.join(jsDir, 'detect-antipatterns-browser.js'));
+  }
+
+  // Read source files (unified skills architecture)
+  const { skills } = readSourceFiles(ROOT_DIR);
+  const patterns = readPatterns(ROOT_DIR);
+  const userInvocableCount = skills.filter(s => s.userInvocable).length;
+  console.log(`📖 Read ${skills.length} skills (${userInvocableCount} user-invocable) and ${patterns.patterns.length + patterns.antipatterns.length} pattern categories\n`);
+
+  // Read skills version from plugin.json
+  const pluginJson = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, '.claude-plugin/plugin.json'), 'utf-8'));
+  const skillsVersion = pluginJson.version;
+
+  // Transform for each provider (unprefixed + prefixed)
+  for (const config of Object.values(PROVIDERS)) {
+    const transform = createTransformer(config);
+    transform(skills, DIST_DIR, { skillsVersion });
+    transform(skills, DIST_DIR, { prefix: 'i-', outputSuffix: '-prefixed', skillsVersion });
+  }
+
+  // Assemble universal directory (unprefixed and prefixed)
+  assembleUniversal(DIST_DIR);
+  assembleUniversal(DIST_DIR, '-prefixed');
+
+  // Create ZIP bundles (individual + universal)
+  await createAllZips(DIST_DIR);
+
+  // Generate static API data and Cloudflare Pages config
+  generateApiData(buildDir, skills, patterns);
+  copyDistToBuild(DIST_DIR, buildDir);
+  generateCFConfig(buildDir);
+
+  // Copy all provider outputs to project root for local testing
+  const syncConfigs = Object.values(PROVIDERS);
+
+  for (const { provider, configDir } of syncConfigs) {
+    const skillsSrc = path.join(DIST_DIR, provider, configDir, 'skills');
+    const skillsDest = path.join(ROOT_DIR, configDir, 'skills');
+
+    if (fs.existsSync(skillsSrc)) {
+      if (fs.existsSync(skillsDest)) fs.rmSync(skillsDest, { recursive: true });
+      copyDirSync(skillsSrc, skillsDest);
+    }
+  }
+
+  // Remove deprecated skill stubs from local harness dirs. They exist
+  // in dist/ so the cleanup script can redirect users, but they should
+  // not clutter the repo's own skill directories.
+  const deprecatedLocalSkills = [
+    'frontend-design', 'teach-impeccable',
+    'arrange', 'normalize', 'onboard', 'extract',
+  ];
+  for (const { configDir } of syncConfigs) {
+    for (const name of deprecatedLocalSkills) {
+      const p = path.join(ROOT_DIR, configDir, 'skills', name);
+      if (fs.existsSync(p)) fs.rmSync(p, { recursive: true, force: true });
+    }
+  }
+
+  console.log(`📋 Synced skills to: ${syncConfigs.map(p => p.configDir).join(', ')}`);
+
+
+  // Generate authoritative counts and validate references
+  const countErrors = generateCounts(ROOT_DIR, skills, buildDir);
+
+  // Cross-validate engine rules against impeccable SKILL.md DON'Ts
+  const validationErrors = validateAntipatternRules(ROOT_DIR);
+
+  // Verify every hand-authored HTML page carries the shared site header
+  const headerErrors = validateSiteHeader(ROOT_DIR);
+
+  // Scan user-facing copy for em dashes
+  const emDashErrors = validateNoEmDashes(ROOT_DIR);
+
+  if (countErrors > 0 || validationErrors > 0 || headerErrors > 0 || emDashErrors > 0) {
+    process.exit(1);
+  }
+
+  console.log('\n✨ Build complete!');
+}
+
+// Run the build
+build();

scripts/generate-extension-icons.js 🔗

@@ -0,0 +1,38 @@
+#!/usr/bin/env node
+
+/**
+ * Generates PNG extension icons from SVG using Puppeteer.
+ *
+ * Run: node scripts/generate-extension-icons.js
+ */
+
+import puppeteer from 'puppeteer';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, '..');
+const ICONS_DIR = path.join(ROOT, 'extension/icons');
+
+const SIZES = [16, 32, 48, 128];
+
+const svgContent = fs.readFileSync(path.join(ICONS_DIR, 'icon.svg'), 'utf-8');
+
+const browser = await puppeteer.launch({ headless: true });
+const page = await browser.newPage();
+
+for (const size of SIZES) {
+  await page.setViewport({ width: size, height: size, deviceScaleFactor: 1 });
+  await page.setContent(`
+    <!DOCTYPE html>
+    <html>
+    <head><style>* { margin: 0; padding: 0; } body { width: ${size}px; height: ${size}px; overflow: hidden; }</style></head>
+    <body>${svgContent.replace('viewBox="0 0 128 128"', `viewBox="0 0 128 128" width="${size}" height="${size}"`)}</body>
+    </html>
+  `);
+  await page.screenshot({ path: path.join(ICONS_DIR, `icon-${size}.png`), omitBackground: true });
+  console.log(`Generated icon-${size}.png`);
+}
+
+await browser.close();

scripts/generate-og-image.js 🔗

@@ -0,0 +1,256 @@
+#!/usr/bin/env node
+
+/**
+ * Generate OG Image
+ *
+ * Renders the OG image using Playwright with proper Google Fonts.
+ * Counts commands dynamically from the source/ directory and composes
+ * the wordmark alongside a real screenshot of the Chrome extension
+ * detection panel from public/assets/extension-detection.png.
+ *
+ * Usage: bun run og-image
+ */
+
+import { chromium } from 'playwright';
+import path from 'path';
+import fs from 'fs';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const ROOT_DIR = path.resolve(__dirname, '..');
+const OUTPUT_PATH = path.join(ROOT_DIR, 'public', 'og-image.jpg');
+const EXTENSION_IMAGE_PATH = path.join(
+  ROOT_DIR,
+  'public',
+  'assets',
+  'extension-detection.png',
+);
+
+// Count user-invocable, non-deprecated skills from source/skills/
+// (In v2.0, commands and skills were unified — every command is a skill.)
+function getCommandCount() {
+  const skillsDir = path.join(ROOT_DIR, 'source', 'skills');
+  if (!fs.existsSync(skillsDir)) return 0;
+
+  let count = 0;
+  for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
+    if (!entry.isDirectory()) continue;
+    const skillFile = path.join(skillsDir, entry.name, 'SKILL.md');
+    if (!fs.existsSync(skillFile)) continue;
+
+    const content = fs.readFileSync(skillFile, 'utf8');
+    const fm = content.match(/^---\n([\s\S]*?)\n---/);
+    if (!fm) continue;
+
+    const frontmatter = fm[1];
+    const isUserInvocable = /^user-invocable:\s*true\s*$/m.test(frontmatter);
+    const isDeprecated = /^description:\s*["']?DEPRECATED/mi.test(frontmatter);
+
+    if (isUserInvocable && !isDeprecated) count++;
+  }
+  return count;
+}
+
+// Load extension screenshot as base64 data URL so setContent is self-contained
+function getExtensionDataUrl() {
+  const buf = fs.readFileSync(EXTENSION_IMAGE_PATH);
+  return `data:image/png;base64,${buf.toString('base64')}`;
+}
+
+async function generateOgImage() {
+  const commands = getCommandCount();
+  const extensionDataUrl = getExtensionDataUrl();
+  console.log(`Detected ${commands} command(s)`);
+
+  const html = `<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <link rel="preconnect" href="https://fonts.googleapis.com">
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+  <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&family=Instrument+Sans:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
+  <style>
+    * { margin: 0; padding: 0; box-sizing: border-box; }
+
+    body {
+      width: 1200px;
+      height: 630px;
+      overflow: hidden;
+      background: #f5f2ee;
+      position: relative;
+      font-family: 'Instrument Sans', system-ui, sans-serif;
+      -webkit-font-smoothing: antialiased;
+      color: #1a1a1a;
+    }
+
+    /* Soft brand glow for depth — subtle magenta accents in opposite corners */
+    body::before {
+      content: '';
+      position: absolute;
+      inset: 0;
+      background:
+        radial-gradient(circle at 15% 8%, rgba(200, 50, 120, 0.07) 0%, transparent 55%),
+        radial-gradient(circle at 90% 95%, rgba(200, 50, 120, 0.05) 0%, transparent 60%);
+      pointer-events: none;
+    }
+
+    .container {
+      position: relative;
+      width: 100%;
+      height: 100%;
+      padding: 72px 80px;
+    }
+
+    .content {
+      position: relative;
+      display: flex;
+      flex-direction: column;
+      justify-content: space-between;
+      height: 100%;
+      max-width: 560px;
+      z-index: 2;
+    }
+
+    .top {
+      display: flex;
+      flex-direction: column;
+      gap: 22px;
+    }
+
+    .title {
+      font-family: 'Cormorant Garamond', Georgia, serif;
+      font-size: 108px;
+      font-weight: 300;
+      font-style: italic;
+      color: #1a1a1a;
+      letter-spacing: -0.03em;
+      line-height: 0.95;
+    }
+
+    .tagline {
+      font-family: 'Cormorant Garamond', Georgia, serif;
+      font-size: 34px;
+      font-weight: 400;
+      font-style: italic;
+      color: #3a3a3a;
+      line-height: 1.3;
+      max-width: 480px;
+    }
+
+    .bottom {
+      display: flex;
+      flex-direction: column;
+      gap: 14px;
+    }
+
+    .features {
+      display: flex;
+      align-items: center;
+      gap: 14px;
+      font-family: 'Space Grotesk', monospace;
+      font-size: 19px;
+      font-weight: 500;
+      color: #1a1a1a;
+      letter-spacing: 0.005em;
+    }
+
+    .feature-sep {
+      color: #c83278;
+      font-weight: 400;
+      font-size: 22px;
+      line-height: 1;
+    }
+
+    .url {
+      font-family: 'Space Grotesk', monospace;
+      font-size: 16px;
+      color: #999;
+      letter-spacing: 0.02em;
+    }
+
+    /* Extension panel — floating product shot, anchored right-center */
+    .panel {
+      position: absolute;
+      right: 54px;
+      top: 50%;
+      width: 500px;
+      transform: translateY(-50%) rotate(-2deg);
+      border-radius: 14px;
+      overflow: hidden;
+      box-shadow:
+        0 1px 2px rgba(0, 0, 0, 0.05),
+        0 6px 14px rgba(0, 0, 0, 0.06),
+        0 24px 48px -10px rgba(30, 15, 25, 0.18),
+        0 48px 100px -20px rgba(200, 50, 120, 0.14);
+      z-index: 1;
+    }
+
+    .panel::after {
+      content: '';
+      position: absolute;
+      inset: 0;
+      border-radius: 14px;
+      border: 1px solid rgba(0, 0, 0, 0.08);
+      pointer-events: none;
+    }
+
+    .panel img {
+      display: block;
+      width: 100%;
+      height: auto;
+    }
+  </style>
+</head>
+<body>
+  <div class="container">
+    <div class="content">
+      <div class="top">
+        <div class="title">Impeccable</div>
+        <div class="tagline">Design fluency for AI harnesses</div>
+      </div>
+      <div class="bottom">
+        <div class="features">
+          <span>${commands} commands</span>
+          <span class="feature-sep">·</span>
+          <span>Chrome extension</span>
+          <span class="feature-sep">·</span>
+          <span>CLI</span>
+        </div>
+        <div class="url">impeccable.style</div>
+      </div>
+    </div>
+    <div class="panel">
+      <img src="${extensionDataUrl}" alt="Impeccable Chrome extension detection panel">
+    </div>
+  </div>
+</body>
+</html>`;
+
+  const browser = await chromium.launch();
+  const page = await browser.newPage({
+    viewport: { width: 1200, height: 630 },
+    deviceScaleFactor: 1,
+  });
+
+  await page.setContent(html, { waitUntil: 'networkidle' });
+
+  // Wait for fonts to load
+  await page.evaluate(() => document.fonts.ready);
+
+  await page.screenshot({
+    path: OUTPUT_PATH,
+    type: 'jpeg',
+    quality: 90,
+  });
+
+  await browser.close();
+
+  const size = (fs.statSync(OUTPUT_PATH).size / 1024).toFixed(0);
+  console.log(`Generated ${OUTPUT_PATH} (${size} KB)`);
+}
+
+generateOgImage().catch((err) => {
+  console.error('Failed to generate OG image:', err);
+  process.exit(1);
+});

scripts/generate-promo-tile.js 🔗

@@ -0,0 +1,145 @@
+#!/usr/bin/env node
+
+/**
+ * Generates the Chrome Web Store small promo tile (440x280) from an SVG template.
+ *
+ * Run: node scripts/generate-promo-tile.js
+ */
+
+import puppeteer from 'puppeteer';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, '..');
+const OUT = path.join(ROOT, 'extension/icons/promo-small.png');
+
+// Brand colors
+const BG = '#0e0d10';
+const BG_TOP = '#161318';
+const MAGENTA = '#cc1b89'; // approximates oklch(55% 0.25 350)
+const TEXT = '#f5f3ef';
+const TEXT_DIM = '#7a7680';
+
+const svg = `
+<svg width="440" height="280" viewBox="0 0 440 280" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
+      <stop offset="0" stop-color="${BG_TOP}"/>
+      <stop offset="1" stop-color="${BG}"/>
+    </linearGradient>
+    <linearGradient id="slop" x1="0" y1="0" x2="1" y2="0">
+      <stop offset="0" stop-color="#a855f7"/>
+      <stop offset="0.5" stop-color="#ec4899"/>
+      <stop offset="1" stop-color="#06b6d4"/>
+    </linearGradient>
+    <radialGradient id="cardGlow" cx="50%" cy="50%" r="60%">
+      <stop offset="0" stop-color="#3a1a4a" stop-opacity="0.6"/>
+      <stop offset="1" stop-color="#1a0f24" stop-opacity="0.3"/>
+    </radialGradient>
+    <filter id="softShadow" x="-20%" y="-20%" width="140%" height="140%">
+      <feGaussianBlur in="SourceAlpha" stdDeviation="6"/>
+      <feOffset dx="0" dy="3" result="offsetblur"/>
+      <feComponentTransfer><feFuncA type="linear" slope="0.4"/></feComponentTransfer>
+      <feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
+    </filter>
+  </defs>
+
+  <!-- Background -->
+  <rect width="440" height="280" fill="url(#bg)"/>
+
+  <!-- Subtle grid texture -->
+  <g opacity="0.04" stroke="${TEXT}" stroke-width="0.5">
+    <line x1="0" y1="70" x2="440" y2="70"/>
+    <line x1="0" y1="140" x2="440" y2="140"/>
+    <line x1="0" y1="210" x2="440" y2="210"/>
+    <line x1="110" y1="0" x2="110" y2="280"/>
+    <line x1="220" y1="0" x2="220" y2="280"/>
+    <line x1="330" y1="0" x2="330" y2="280"/>
+  </g>
+
+  <!-- Brand wordmark (top-left) -->
+  <g transform="translate(28, 26)">
+    <rect width="24" height="24" rx="4.5" fill="#1a1a1a"/>
+    <!-- Slash matches the brand icon: (76,24)→(52,104) in 128 viewBox, scaled to 24 -->
+    <line x1="14.25" y1="4.5" x2="9.75" y2="19.5" stroke="${TEXT}" stroke-width="1.3" stroke-linecap="round"/>
+    <text x="34" y="17" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif" font-size="16" font-weight="600" fill="${TEXT}" letter-spacing="-0.01em">Impeccable</text>
+  </g>
+
+  <!-- Small badge top-right -->
+  <g transform="translate(338, 28)">
+    <rect width="74" height="20" rx="10" fill="${TEXT}" fill-opacity="0.06"/>
+    <text x="37" y="14" text-anchor="middle" font-family="-apple-system, system-ui, sans-serif" font-size="10" font-weight="500" fill="${TEXT_DIM}" letter-spacing="0.04em">DEVTOOLS</text>
+  </g>
+
+  <!-- Demo card group (centered, slightly offset) -->
+  <g transform="translate(64, 92)">
+    <!-- Faux UI card -->
+    <g filter="url(#softShadow)">
+      <rect x="0" y="0" width="312" height="92" rx="14" fill="#1a1422"/>
+      <rect x="0" y="0" width="312" height="92" rx="14" fill="url(#cardGlow)"/>
+    </g>
+
+    <!-- Sparkles inside the card (more AI slop vibes) -->
+    <text x="32" y="56" font-family="-apple-system, system-ui, sans-serif" font-size="20" fill="#fbbf24">✨</text>
+    <text x="268" y="56" font-family="-apple-system, system-ui, sans-serif" font-size="20" fill="#fbbf24">✨</text>
+
+    <!-- Gradient text headline (the slop being detected) -->
+    <text x="156" y="52" text-anchor="middle"
+          font-family="-apple-system, BlinkMacSystemFont, system-ui, sans-serif"
+          font-size="20" font-weight="700"
+          fill="url(#slop)">AI-Powered Magic</text>
+
+    <!-- Subline -->
+    <text x="156" y="72" text-anchor="middle"
+          font-family="-apple-system, system-ui, sans-serif"
+          font-size="10" fill="#9b94a8" letter-spacing="0.02em">Reimagining the future of everything</text>
+
+    <!-- Impeccable magenta outline (offset by 2px outside the card)
+         Path so top-left corner is square (where label meets it) -->
+    <path d="M -4 -4 L 312 -4 Q 316 -4 316 0 L 316 92 Q 316 96 312 96 L 0 96 Q -4 96 -4 92 L -4 -4 Z"
+          fill="none" stroke="${MAGENTA}" stroke-width="2" stroke-linejoin="round"/>
+
+    <!-- Label tab on top, flush with outline's outer edge.
+         Label extends to x=-5 (matching outline visible left edge) and y=-3 (covering the outline's top stroke). -->
+    <path d="M -5 -3 L -5 -22 Q -5 -26 -1 -26 L 113 -26 Q 117 -26 117 -22 L 117 -3 Z"
+          fill="${MAGENTA}"/>
+    <text x="2" y="-10"
+          font-family="-apple-system, BlinkMacSystemFont, system-ui, sans-serif"
+          font-size="11" font-weight="600" fill="white" letter-spacing="0.01em">
+      ✦ gradient text
+    </text>
+  </g>
+
+  <!-- Tagline at bottom -->
+  <text x="28" y="244"
+        font-family="-apple-system, BlinkMacSystemFont, system-ui, sans-serif"
+        font-size="15" font-weight="600" fill="${TEXT}" letter-spacing="-0.01em">
+    Detect AI slop in any web page.
+  </text>
+  <text x="28" y="262"
+        font-family="-apple-system, system-ui, sans-serif"
+        font-size="11" fill="${TEXT_DIM}" letter-spacing="0.01em">
+    24 detections · Open DevTools and see what needs fixing.
+  </text>
+</svg>
+`;
+
+const browser = await puppeteer.launch({ headless: true });
+const page = await browser.newPage();
+await page.setViewport({ width: 440, height: 280, deviceScaleFactor: 1 });
+await page.setContent(`
+  <!DOCTYPE html>
+  <html>
+  <head><style>
+    * { margin: 0; padding: 0; }
+    body { width: 440px; height: 280px; overflow: hidden; }
+  </style></head>
+  <body>${svg}</body>
+  </html>
+`);
+await page.screenshot({ path: OUT, omitBackground: false });
+await browser.close();
+
+console.log(`Generated ${path.relative(ROOT, OUT)}`);

scripts/lib/render-markdown.js 🔗

@@ -0,0 +1,156 @@
+/**
+ * Markdown → HTML rendering for sub-pages.
+ *
+ * Wraps `marked` with a custom link renderer that resolves cross-references
+ * between skill bodies and their references, and emits stable heading slugs
+ * so anti-pattern → skill section anchors work.
+ *
+ * Skeleton in commit 1. Link resolution and heading slugger are wired up in
+ * commit 3 (skills generator) when the data model lands.
+ */
+
+import { marked } from 'marked';
+
+/**
+ * Slugify a heading text into a stable anchor id.
+ * Matches the convention: lowercase, strip non-alphanum, spaces → dashes.
+ *
+ * @param {string} text
+ * @returns {string}
+ */
+export function slugify(text) {
+  return String(text)
+    .toLowerCase()
+    .trim()
+    .replace(/[^\w\s-]/g, '')
+    .replace(/[\s_]+/g, '-')
+    .replace(/^-+|-+$/g, '');
+}
+
+/**
+ * Build a marked renderer configured for impeccable's skill/tutorial bodies.
+ *
+ * @param {object} opts
+ * @param {Set<string>} [opts.knownSkillIds] - slugs of skills the site knows about; unknown /name mentions render as plain text
+ * @param {string}      [opts.currentSkillId] - when rendering a skill body, resolve `reference/foo.md` to `#reference-foo` on the current page
+ * @returns {import('marked').Renderer}
+ */
+export function createRenderer({ knownSkillIds = new Set(), currentSkillId = null } = {}) {
+  const renderer = new marked.Renderer();
+
+  // Heading slugger — stable ids so we can anchor-link from elsewhere.
+  // Supports {#custom-id} suffix (kramdown/pandoc style) for explicit anchors.
+  renderer.heading = ({ tokens, depth }) => {
+    const raw = tokens.map((t) => t.raw || '').join('');
+    const customIdMatch = raw.match(/\s*\{#([a-z0-9_-]+)\}\s*$/i);
+    let id, text;
+    if (customIdMatch) {
+      id = customIdMatch[1];
+      // Strip the {#id} suffix from the rendered text
+      const cleanRaw = raw.slice(0, customIdMatch.index);
+      text = renderer.parser.parseInline(marked.lexer(cleanRaw, { gfm: true })[0]?.tokens || tokens);
+    } else {
+      id = slugify(raw);
+      text = renderer.parser.parseInline(tokens);
+    }
+    return `<h${depth} id="${id}">${text}</h${depth}>\n`;
+  };
+
+  // Link resolver.
+  renderer.link = ({ href, title, tokens }) => {
+    const text = renderer.parser.parseInline(tokens);
+    const resolved = resolveHref(href, { knownSkillIds, currentSkillId });
+    const titleAttr = title ? ` title="${escapeAttr(title)}"` : '';
+    const relAttr = resolved.external ? ' target="_blank" rel="noopener"' : '';
+    return `<a href="${escapeAttr(resolved.href)}"${titleAttr}${relAttr}>${text}</a>`;
+  };
+
+  // Fenced code blocks — minimal glass-terminal styling, no syntax highlighter in v1.
+  // Wrapped in a container with a copy button; click handling lives in the
+  // page-level inline script added by render-page.js.
+  renderer.code = ({ text, lang }) => {
+    const langClass = lang ? ` code-block--${escapeAttr(lang)}` : '';
+    const copyValue = escapeAttr(text);
+    return `<div class="code-block-wrap"><pre class="code-block${langClass}"><code>${escapeHtml(text)}</code></pre><button class="code-block-copy" type="button" data-copy="${copyValue}" aria-label="Copy to clipboard"></button></div>\n`;
+  };
+
+  return renderer;
+}
+
+/**
+ * Resolve a markdown link href against the site's URL scheme.
+ *
+ * - `http(s)://…` → unchanged, external
+ * - `reference/foo.md` → `#reference-foo` on current skill page
+ * - `/skill-id` (known) → `/skills/skill-id`
+ * - `#anchor` → unchanged (in-page anchor)
+ * - anything else → unchanged (will be caught by build warnings later)
+ *
+ * @param {string} href
+ * @param {{ knownSkillIds: Set<string>, currentSkillId: string|null }} ctx
+ * @returns {{ href: string, external: boolean }}
+ */
+function resolveHref(href, { knownSkillIds, currentSkillId }) {
+  if (!href) return { href: '', external: false };
+
+  // External links
+  if (/^https?:\/\//i.test(href) || /^mailto:/i.test(href)) {
+    return { href, external: true };
+  }
+
+  // In-page anchor
+  if (href.startsWith('#')) {
+    return { href, external: false };
+  }
+
+  // reference/foo.md → #reference-foo on the current skill page
+  const refMatch = href.match(/^reference\/([a-z0-9-]+)\.md$/i);
+  if (refMatch && currentSkillId) {
+    return { href: `#reference-${refMatch[1].toLowerCase()}`, external: false };
+  }
+
+  // /skill-id mentioned in prose (e.g. "run /polish")
+  const slashMatch = href.match(/^\/([a-z0-9-]+)$/i);
+  if (slashMatch && knownSkillIds.has(slashMatch[1])) {
+    return { href: `/skills/${slashMatch[1]}`, external: false };
+  }
+
+  // [text](other-skill) → /skills/other-skill
+  if (/^[a-z0-9-]+$/i.test(href) && knownSkillIds.has(href)) {
+    return { href: `/skills/${href}`, external: false };
+  }
+
+  // Unknown — pass through. Generator can warn separately.
+  return { href, external: false };
+}
+
+/**
+ * Render a markdown string to HTML.
+ *
+ * @param {string} markdown
+ * @param {object} [opts]
+ * @param {Set<string>} [opts.knownSkillIds]
+ * @param {string}      [opts.currentSkillId]
+ * @returns {string} HTML
+ */
+export function renderMarkdown(markdown, opts = {}) {
+  const renderer = createRenderer(opts);
+  return marked.parse(markdown, {
+    renderer,
+    gfm: true,
+    breaks: false,
+  });
+}
+
+function escapeHtml(str) {
+  return String(str)
+    .replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/"/g, '&quot;')
+    .replace(/'/g, '&#39;');
+}
+
+function escapeAttr(str) {
+  return String(str).replace(/"/g, '&quot;');
+}

scripts/lib/render-page.js 🔗

@@ -0,0 +1,247 @@
+/**
+ * Page template wrapper for generated sub-pages.
+ *
+ * Reads the shared site header partial once and wraps content bodies with
+ * a minimal HTML scaffold that imports tokens.css + sub-pages.css.
+ *
+ * Used by scripts/build-sub-pages.js (wired up in commit 3).
+ */
+
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT_DIR = path.resolve(__dirname, '..', '..');
+const HEADER_PARTIAL = path.join(ROOT_DIR, 'content', 'site', 'partials', 'header.html');
+
+let cachedHeader = null;
+
+/**
+ * Read the shared site header partial.
+ * Cached after first read.
+ */
+export function readHeaderPartial() {
+  if (cachedHeader === null) {
+    cachedHeader = fs.readFileSync(HEADER_PARTIAL, 'utf8').trim();
+  }
+  return cachedHeader;
+}
+
+/**
+ * Mark a nav item as current by adding aria-current="page" and removing
+ * the default nav href state. Matches on `data-nav="{activeNav}"`.
+ *
+ * @param {string} headerHtml
+ * @param {string} activeNav - one of: home, skills, anti-patterns, tutorials, gallery, github
+ * @returns {string}
+ */
+export function applyActiveNav(headerHtml, activeNav) {
+  if (!activeNav) return headerHtml;
+  return headerHtml.replace(
+    new RegExp(`data-nav="${activeNav}"`, 'g'),
+    `data-nav="${activeNav}" aria-current="page"`,
+  );
+}
+
+/**
+ * Wrap body HTML in a full page shell.
+ *
+ * @param {object} opts
+ * @param {string}   opts.title         - <title> text
+ * @param {string}   opts.description   - meta description
+ * @param {string}   opts.bodyHtml      - main content HTML (will be placed inside <main>)
+ * @param {string}   [opts.activeNav]   - which nav item to mark current
+ * @param {string}   [opts.canonicalPath] - relative URL path for <link rel="canonical">
+ * @param {string}   [opts.extraHead]   - raw HTML to inject into <head>
+ * @param {string}   [opts.bodyClass]   - optional class on <body>
+ * @param {number}   [opts.assetDepth]  - how many `..` to prepend for Bun's HTML loader to resolve on-disk paths. 1 = page is one dir deep under public/ (e.g. public/skills/polish.html). Defaults to 1.
+ * @returns {string} full HTML document
+ */
+export function renderPage({
+  title,
+  description,
+  bodyHtml,
+  activeNav,
+  canonicalPath,
+  extraHead = '',
+  bodyClass = 'sub-page',
+  assetDepth = 1,
+}) {
+  const header = applyActiveNav(readHeaderPartial(), activeNav);
+  const safeTitle = escapeHtml(title);
+  const safeDesc = escapeAttr(description || '');
+  const canonical = canonicalPath
+    ? `<link rel="canonical" href="https://impeccable.style${canonicalPath}">`
+    : '';
+
+  // Relative prefix for on-disk resolution by Bun's HTML loader.
+  // Bun rewrites these to hashed absolute URLs at build time, so runtime
+  // serving works regardless of the request path.
+  const rel = assetDepth > 0 ? '../'.repeat(assetDepth) : './';
+
+  return `<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>${safeTitle}</title>
+  <meta name="description" content="${safeDesc}">
+  <meta name="theme-color" content="#fafafa">
+  ${canonical}
+  <link rel="icon" type="image/svg+xml" href="${rel}favicon.svg">
+  <link rel="preconnect" href="https://fonts.googleapis.com">
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+  <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Instrument+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
+  <link rel="stylesheet" href="${rel}css/sub-pages.css">
+  ${extraHead}
+</head>
+<body class="${bodyClass}">
+  <a href="#main" class="skip-link">Skip to content</a>
+  ${header}
+  <main id="main">
+${bodyHtml}
+  </main>
+  <script>
+    // Copy buttons on rendered code blocks
+    document.addEventListener('click', (e) => {
+      const btn = e.target.closest('[data-copy]');
+      if (!btn) return;
+      const text = btn.getAttribute('data-copy');
+      if (!text) return;
+      navigator.clipboard.writeText(text).then(() => {
+        btn.classList.add('is-copied');
+        setTimeout(() => btn.classList.remove('is-copied'), 1500);
+      }).catch(() => {});
+    });
+
+    // Mobile sidebar toggle (shown on narrow viewports, hidden on desktop).
+    document.addEventListener('click', (e) => {
+      const toggle = e.target.closest('.skills-sidebar-toggle');
+      if (!toggle) return;
+      const expanded = toggle.getAttribute('aria-expanded') === 'true';
+      toggle.setAttribute('aria-expanded', String(!expanded));
+    });
+
+    // Before/after split-compare: drag on touch, hover OR drag on mouse.
+    // Pointer events attach to the padded .split-comparison wrapper so
+    // there is a ~20px invisible buffer around the visible box. The
+    // divider only snaps back when the pointer leaves that outer buffer.
+    (function initSplitCompare() {
+      const wrappers = document.querySelectorAll('.split-comparison');
+      if (wrappers.length === 0) return;
+      const hasHover = matchMedia('(hover: hover)').matches;
+      const DEFAULT_POSITION = 50;
+
+      for (const wrapper of wrappers) {
+        const container = wrapper.querySelector('.split-container');
+        const splitAfter = wrapper.querySelector('.split-after');
+        const splitDivider = wrapper.querySelector('.split-divider');
+        if (!container || !splitAfter || !splitDivider) continue;
+
+        const tanAngle = Math.tan(10 * Math.PI / 180);
+        let skewOffset = 8;
+        const recalcSkew = () => {
+          const r = container.getBoundingClientRect();
+          if (r.width > 0 && r.height > 0) {
+            skewOffset = 50 * r.height * tanAngle / r.width;
+          }
+        };
+        recalcSkew();
+        window.addEventListener('resize', recalcSkew, { passive: true });
+
+        let targetX = DEFAULT_POSITION;
+        let currentX = DEFAULT_POSITION;
+        let rafId = null;
+
+        const paint = (pct) => {
+          const x = Math.max(-skewOffset, Math.min(100 + skewOffset, pct));
+          splitAfter.style.clipPath =
+            \`polygon(\${x + skewOffset}% 0%, 100% 0%, 100% 100%, \${x - skewOffset}% 100%)\`;
+          splitDivider.style.left = \`\${x}%\`;
+        };
+
+        const step = () => {
+          currentX += (targetX - currentX) * 0.2;
+          if (Math.abs(targetX - currentX) < 0.1) {
+            currentX = targetX;
+            rafId = null;
+          } else {
+            rafId = requestAnimationFrame(step);
+          }
+          paint(currentX);
+        };
+
+        const setTarget = (pct) => {
+          targetX = pct;
+          if (rafId === null) rafId = requestAnimationFrame(step);
+        };
+
+        paint(DEFAULT_POSITION);
+
+        // Percentage is always relative to the VISIBLE .split-container,
+        // not the padded .split-comparison wrapper. The pointer event
+        // target is the wrapper but the clip-path math uses the inner box.
+        const pctFromClientX = (clientX) => {
+          const rect = container.getBoundingClientRect();
+          return ((clientX - rect.left) / rect.width) * 100;
+        };
+
+        let hovering = false;
+        let dragging = false;
+
+        wrapper.addEventListener('pointerenter', (e) => {
+          if (hasHover && e.pointerType === 'mouse') {
+            hovering = true;
+          }
+        });
+
+        wrapper.addEventListener('pointerdown', (e) => {
+          dragging = true;
+          wrapper.setPointerCapture(e.pointerId);
+          setTarget(pctFromClientX(e.clientX));
+        });
+
+        wrapper.addEventListener('pointermove', (e) => {
+          if (dragging || hovering) {
+            setTarget(pctFromClientX(e.clientX));
+          }
+        });
+
+        const endDrag = (e) => {
+          if (dragging) {
+            dragging = false;
+            try { wrapper.releasePointerCapture(e.pointerId); } catch {}
+          }
+        };
+
+        wrapper.addEventListener('pointerup', endDrag);
+        wrapper.addEventListener('pointercancel', endDrag);
+
+        wrapper.addEventListener('pointerleave', (e) => {
+          endDrag(e);
+          if (hovering) {
+            hovering = false;
+            setTarget(DEFAULT_POSITION);
+          }
+        });
+      }
+    })();
+  </script>
+</body>
+</html>
+`;
+}
+
+function escapeHtml(str) {
+  return String(str || '')
+    .replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/"/g, '&quot;')
+    .replace(/'/g, '&#39;');
+}
+
+function escapeAttr(str) {
+  return String(str || '').replace(/"/g, '&quot;');
+}

scripts/lib/sub-pages-data.js 🔗

@@ -0,0 +1,248 @@
+/**
+ * Build the data model used by the skill / anti-pattern / tutorial page
+ * generators.
+ *
+ * Single source of truth:
+ * - source/skills/{id}/SKILL.md          → skill frontmatter + body
+ * - source/skills/{id}/reference/*.md     → skill reference files
+ * - src/detect-antipatterns.mjs           → ANTIPATTERNS array (parsed)
+ * - content/site/skills/{id}.md           → optional editorial wrapper
+ * - content/site/tutorials/{slug}.md       → full tutorial content
+ */
+
+import fs from 'node:fs';
+import path from 'node:path';
+import { pathToFileURL } from 'node:url';
+import { readSourceFiles, parseFrontmatter } from './utils.js';
+import {
+  DETECTION_LAYERS,
+  VISUAL_EXAMPLES,
+  LLM_ONLY_RULES,
+  GALLERY_ITEMS,
+} from '../../content/site/anti-patterns-catalog.js';
+
+export {
+  LAYER_LABELS,
+  LAYER_DESCRIPTIONS,
+  GALLERY_ITEMS,
+} from '../../content/site/anti-patterns-catalog.js';
+
+/**
+ * Skills that should be excluded from the index and not get a detail page.
+ * These are deprecated shims or internal skills that users shouldn't browse.
+ */
+const EXCLUDED_SKILLS = new Set([
+  'frontend-design',   // deprecated, renamed to impeccable
+  'teach-impeccable',  // deprecated, folded into /impeccable teach
+  'arrange',           // renamed to layout
+  'normalize',         // merged into /polish
+  'onboard',           // merged into /harden
+  'extract',           // merged into /impeccable extract
+]);
+
+/**
+ * Hand-curated category map for user-invocable skills.
+ * Mirrors public/js/data.js commandCategories. Validated below: the
+ * generator fails if any user-invocable skill is missing from this map.
+ */
+const SKILL_CATEGORIES = {
+  // CREATE - build something new
+  impeccable: 'create',
+  shape: 'create',
+  // EVALUATE - review and assess
+  critique: 'evaluate',
+  audit: 'evaluate',
+  // REFINE - improve existing design
+  typeset: 'refine',
+  layout: 'refine',
+  colorize: 'refine',
+  animate: 'refine',
+  delight: 'refine',
+  bolder: 'refine',
+  quieter: 'refine',
+  overdrive: 'refine',
+  // SIMPLIFY - reduce and clarify
+  distill: 'simplify',
+  clarify: 'simplify',
+  adapt: 'simplify',
+  // HARDEN - production-ready
+  polish: 'harden',
+  optimize: 'harden',
+  harden: 'harden',
+};
+
+export const CATEGORY_ORDER = ['create', 'evaluate', 'refine', 'simplify', 'harden'];
+
+export const CATEGORY_LABELS = {
+  create: 'Create',
+  evaluate: 'Evaluate',
+  refine: 'Refine',
+  simplify: 'Simplify',
+  harden: 'Harden',
+  system: 'System',
+};
+
+export const CATEGORY_DESCRIPTIONS = {
+  create: 'Build something new, from a blank page to a working feature.',
+  evaluate: 'Review what you have. Score it, critique it, find what to fix.',
+  refine: 'Improve one dimension at a time: type, layout, color, motion.',
+  simplify: 'Strip complexity. Remove what does not earn its place.',
+  harden: 'Make it production-ready. Edge cases, performance, polish.',
+  system: 'Setup and tooling. Design system work, extraction, organization.',
+};
+
+/**
+ * Parse the ANTIPATTERNS array out of src/detect-antipatterns.mjs.
+ * Mirrors the trick in scripts/build.js validateAntipatternRules() so we
+ * don't have to run the browser-only module.
+ */
+export function readAntipatternRules(rootDir) {
+  const detectPath = path.join(rootDir, 'src/detect-antipatterns.mjs');
+  const src = fs.readFileSync(detectPath, 'utf-8');
+  const match = src.match(/const ANTIPATTERNS = \[([\s\S]*?)\n\];/);
+  if (!match) {
+    throw new Error(`Could not extract ANTIPATTERNS from ${detectPath}`);
+  }
+  // eslint-disable-next-line no-new-func
+  return new Function(`return [${match[1]}]`)();
+}
+
+/**
+ * Read an optional editorial wrapper file for a skill or tutorial.
+ * Returns { frontmatter, body } or null if the file doesn't exist.
+ */
+export function readEditorialWrapper(contentDir, kind, slug) {
+  const filePath = path.join(contentDir, kind, `${slug}.md`);
+  if (!fs.existsSync(filePath)) return null;
+  const content = fs.readFileSync(filePath, 'utf-8');
+  return parseFrontmatter(content);
+}
+
+/**
+ * Load the per-command before/after demo data from public/js/demos/commands.
+ * Returns a { [skillId]: { id, caption, before, after } } map.
+ * Skills without a demo file are simply missing from the map; the caller
+ * should treat a missing entry as "no demo".
+ */
+export async function loadCommandDemos(rootDir) {
+  const demosDir = path.join(rootDir, 'public/js/demos/commands');
+  if (!fs.existsSync(demosDir)) return {};
+
+  const demos = {};
+  const files = fs
+    .readdirSync(demosDir)
+    .filter((f) => f.endsWith('.js') && f !== 'index.js');
+
+  for (const file of files) {
+    const full = path.join(demosDir, file);
+    try {
+      const mod = await import(pathToFileURL(full).href);
+      const demo = mod.default;
+      if (demo && demo.id) {
+        demos[demo.id] = demo;
+      }
+    } catch (err) {
+      // Demo files occasionally import other demo modules or use features
+      // that don't survive dynamic import. Log and move on rather than
+      // failing the whole generator.
+      console.warn(`[sub-pages] Could not load demo ${file}: ${err.message}`);
+    }
+  }
+  return demos;
+}
+
+/**
+ * Build the full sub-page data model.
+ *
+ * @param {string} rootDir - repo root
+ * @returns {{
+ *   skills: Array,
+ *   skillsByCategory: Record<string, Array>,
+ *   knownSkillIds: Set<string>,
+ *   rules: Array,
+ *   tutorials: Array,
+ * }}
+ */
+export async function buildSubPageData(rootDir) {
+  const { skills: rawSkills } = readSourceFiles(rootDir);
+  const contentDir = path.join(rootDir, 'content/site');
+  const commandDemos = await loadCommandDemos(rootDir);
+
+  // Filter to user-invocable, non-deprecated skills.
+  const skills = rawSkills
+    .filter((s) => s.userInvocable && !EXCLUDED_SKILLS.has(s.name))
+    .map((s) => {
+      const category = SKILL_CATEGORIES[s.name];
+      const editorial = readEditorialWrapper(contentDir, 'skills', s.name);
+      const demo = commandDemos[s.name] || null;
+      return {
+        id: s.name,
+        name: s.name,
+        description: s.description,
+        argumentHint: s.argumentHint,
+        category,
+        body: s.body,
+        references: s.references,
+        editorial, // may be null
+        demo, // may be null (e.g. /shape has no demo)
+      };
+    })
+    .sort((a, b) => a.name.localeCompare(b.name));
+
+  // Validate the category map covers every user-invocable skill.
+  const missing = skills.filter((s) => !s.category).map((s) => s.id);
+  if (missing.length > 0) {
+    throw new Error(
+      `SKILL_CATEGORIES in scripts/lib/sub-pages-data.js is missing entries for: ${missing.join(', ')}`,
+    );
+  }
+
+  const knownSkillIds = new Set(skills.map((s) => s.id));
+
+  const skillsByCategory = {};
+  for (const cat of CATEGORY_ORDER) skillsByCategory[cat] = [];
+  for (const skill of skills) skillsByCategory[skill.category].push(skill);
+
+  // Anti-pattern rules, enriched with catalog metadata and merged with
+  // LLM-only rules from the skill's DON'T list.
+  const detectedRules = readAntipatternRules(rootDir).map((r) => ({
+    ...r,
+    layer: DETECTION_LAYERS[r.id] || 'cli',
+    visual: VISUAL_EXAMPLES[r.id] || null,
+  }));
+  const llmRules = LLM_ONLY_RULES.map((r) => ({
+    ...r,
+    layer: 'llm',
+    visual: VISUAL_EXAMPLES[r.id] || null,
+  }));
+  const rules = [...detectedRules, ...llmRules];
+
+  // Tutorials: each required file in content/site/tutorials/.
+  const tutorialsDir = path.join(contentDir, 'tutorials');
+  const tutorials = [];
+  if (fs.existsSync(tutorialsDir)) {
+    const files = fs.readdirSync(tutorialsDir).filter((f) => f.endsWith('.md'));
+    for (const file of files) {
+      const slug = path.basename(file, '.md');
+      const raw = fs.readFileSync(path.join(tutorialsDir, file), 'utf-8');
+      const { frontmatter, body } = parseFrontmatter(raw);
+      tutorials.push({
+        slug,
+        title: frontmatter.title || slug,
+        description: frontmatter.description || '',
+        tagline: frontmatter.tagline || '',
+        order: frontmatter.order ? Number(frontmatter.order) : 99,
+        body,
+      });
+    }
+    tutorials.sort((a, b) => a.order - b.order);
+  }
+
+  return {
+    skills,
+    skillsByCategory,
+    knownSkillIds,
+    rules,
+    tutorials,
+  };
+}

scripts/lib/transformers/factory.js 🔗

@@ -0,0 +1,132 @@
+import path from 'path';
+import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders, prefixSkillReferences, PROVIDER_PLACEHOLDERS } from '../utils.js';
+
+/**
+ * Map from frontmatter field name to extraction spec.
+ *
+ * - sourceKey: property name on the skill object
+ * - yamlKey: key name in YAML frontmatter
+ * - condition: if provided, field is only emitted when this returns true
+ * - value: if provided, use this instead of skill[sourceKey]
+ */
+const FIELD_SPECS = {
+  'user-invocable': {
+    sourceKey: 'userInvocable',
+    yamlKey: 'user-invocable',
+    condition: (skill) => skill.userInvocable,
+    value: () => true,
+  },
+  'argument-hint': {
+    sourceKey: 'argumentHint',
+    yamlKey: 'argument-hint',
+    condition: (skill) => skill.userInvocable && skill.argumentHint,
+  },
+  license: {
+    sourceKey: 'license',
+    yamlKey: 'license',
+  },
+  compatibility: {
+    sourceKey: 'compatibility',
+    yamlKey: 'compatibility',
+  },
+  metadata: {
+    sourceKey: 'metadata',
+    yamlKey: 'metadata',
+  },
+  'allowed-tools': {
+    sourceKey: 'allowedTools',
+    yamlKey: 'allowed-tools',
+  },
+};
+
+/**
+ * Create a transformer function for a given provider config.
+ *
+ * @param {Object} config - Provider configuration from providers.js
+ * @returns {Function} transform(skills, distDir, options?)
+ */
+export function createTransformer(config) {
+  const { provider, configDir, displayName, frontmatterFields = [], bodyTransform, placeholderProvider } = config;
+  const placeholderKey = placeholderProvider || provider;
+
+  const activeFields = frontmatterFields
+    .map((name) => FIELD_SPECS[name])
+    .filter(Boolean);
+
+  return function transform(skills, distDir, options = {}) {
+    const { prefix = '', outputSuffix = '', skillsVersion = '' } = options;
+    const providerDir = path.join(distDir, `${provider}${outputSuffix}`);
+    const skillsDir = path.join(providerDir, `${configDir}/skills`);
+
+    cleanDir(providerDir);
+    ensureDir(skillsDir);
+
+    const allSkillNames = skills.map((s) => s.name);
+    const commandNames = skills
+      .filter((s) => s.userInvocable)
+      .map((s) => `${prefix}${s.name}`);
+
+    let refCount = 0;
+    let scriptCount = 0;
+
+    for (const skill of skills) {
+      const skillName = `${prefix}${skill.name}`;
+      const skillDir = path.join(skillsDir, skillName);
+
+      // Build frontmatter
+      const frontmatterObj = {
+        name: skillName,
+        description: skill.description,
+      };
+      if (skillsVersion) frontmatterObj.version = skillsVersion;
+
+      for (const spec of activeFields) {
+        if (spec.condition && !spec.condition(skill)) continue;
+        const val = spec.value ? spec.value(skill) : skill[spec.sourceKey];
+        if (val) frontmatterObj[spec.yamlKey] = val;
+      }
+
+      const frontmatter = generateYamlFrontmatter(frontmatterObj);
+
+      // Build body
+      const cmdPrefix = (PROVIDER_PLACEHOLDERS[placeholderKey] || {}).command_prefix || '/';
+      let skillBody = replacePlaceholders(skill.body, placeholderKey, commandNames, allSkillNames);
+
+      // Replace {{scripts_path}} with provider-aware path to skill's scripts directory
+      const scriptsPath = `${configDir}/skills/${skillName}/scripts`;
+      skillBody = skillBody.replace(/\{\{scripts_path\}\}/g, scriptsPath);
+      if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames, cmdPrefix);
+      if (bodyTransform) skillBody = bodyTransform(skillBody, skill);
+
+      const content = `${frontmatter}\n\n${skillBody}`;
+      writeFile(path.join(skillDir, 'SKILL.md'), content);
+
+      // Copy reference files
+      if (skill.references && skill.references.length > 0) {
+        const refDir = path.join(skillDir, 'reference');
+        ensureDir(refDir);
+        for (const ref of skill.references) {
+          const refContent = replacePlaceholders(ref.content, placeholderKey, [], allSkillNames);
+          writeFile(path.join(refDir, `${ref.name}.md`), refContent);
+          refCount++;
+        }
+      }
+
+      // Copy script files
+      if (skill.scripts && skill.scripts.length > 0) {
+        const scriptsOutDir = path.join(skillDir, 'scripts');
+        ensureDir(scriptsOutDir);
+        for (const script of skill.scripts) {
+          writeFile(path.join(scriptsOutDir, script.name), script.content);
+          scriptCount++;
+        }
+      }
+    }
+
+    const userInvocableCount = skills.filter((s) => s.userInvocable).length;
+    const refInfo = refCount > 0 ? ` (${refCount} reference files)` : '';
+    const scriptInfo = scriptCount > 0 ? ` (${scriptCount} script files)` : '';
+    const prefixInfo = prefix ? ` [${prefix}prefixed]` : '';
+    console.log(`✓ ${displayName}${prefixInfo}: ${skills.length} skills (${userInvocableCount} user-invocable)${refInfo}${scriptInfo}`);
+  };
+}

scripts/lib/transformers/index.js 🔗

@@ -0,0 +1,14 @@
+import { createTransformer } from './factory.js';
+import { PROVIDERS } from './providers.js';
+
+export const transformCursor = createTransformer(PROVIDERS.cursor);
+export const transformClaudeCode = createTransformer(PROVIDERS['claude-code']);
+export const transformGemini = createTransformer(PROVIDERS.gemini);
+export const transformCodex = createTransformer(PROVIDERS.codex);
+export const transformAgents = createTransformer(PROVIDERS.agents);
+export const transformKiro = createTransformer(PROVIDERS.kiro);
+export const transformOpenCode = createTransformer(PROVIDERS.opencode);
+export const transformPi = createTransformer(PROVIDERS.pi);
+export const transformRovoDev = createTransformer(PROVIDERS['rovo-dev']);
+
+export { createTransformer, PROVIDERS };

scripts/lib/transformers/providers.js 🔗

@@ -0,0 +1,79 @@
+/**
+ * Provider configurations for the transformer factory.
+ *
+ * Each config specifies:
+ * - provider: key into PROVIDER_PLACEHOLDERS (e.g. 'claude-code')
+ * - configDir: dot-directory name (e.g. '.claude')
+ * - displayName: human-readable name for log output (e.g. 'Claude Code')
+ * - frontmatterFields: which optional fields to emit beyond name + description
+ * - bodyTransform: optional function (body, skill) => transformed body
+ */
+export const PROVIDERS = {
+  cursor: {
+    provider: 'cursor',
+    configDir: '.cursor',
+    displayName: 'Cursor',
+    frontmatterFields: ['license', 'compatibility', 'metadata'],
+  },
+  'claude-code': {
+    provider: 'claude-code',
+    configDir: '.claude',
+    displayName: 'Claude Code',
+    frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata', 'allowed-tools'],
+  },
+  gemini: {
+    provider: 'gemini',
+    configDir: '.gemini',
+    displayName: 'Gemini',
+    frontmatterFields: [],
+  },
+  codex: {
+    provider: 'codex',
+    configDir: '.codex',
+    displayName: 'Codex',
+    frontmatterFields: ['argument-hint', 'license'],
+  },
+  agents: {
+    provider: 'agents',
+    configDir: '.agents',
+    displayName: 'Agents',
+    frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata'],
+  },
+  kiro: {
+    provider: 'kiro',
+    configDir: '.kiro',
+    displayName: 'Kiro',
+    frontmatterFields: ['license', 'compatibility', 'metadata'],
+  },
+  opencode: {
+    provider: 'opencode',
+    configDir: '.opencode',
+    displayName: 'OpenCode',
+    frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata', 'allowed-tools'],
+  },
+  pi: {
+    provider: 'pi',
+    configDir: '.pi',
+    displayName: 'Pi',
+    frontmatterFields: ['license', 'compatibility', 'metadata', 'allowed-tools'],
+  },
+  'trae-cn': {
+    provider: 'trae-cn',
+    configDir: '.trae-cn',
+    displayName: 'Trae China',
+    placeholderProvider: 'trae',
+    frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata'],
+  },
+  trae: {
+    provider: 'trae',
+    configDir: '.trae',
+    displayName: 'Trae',
+    frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata'],
+  },
+  'rovo-dev': {
+    provider: 'rovo-dev',
+    configDir: '.rovodev',
+    displayName: 'Rovo Dev',
+    frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata', 'allowed-tools'],
+  },
+};

scripts/lib/transformers/shared.js 🔗

@@ -0,0 +1,81 @@
+import path from 'path';
+import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders, prefixSkillReferences } from '../utils.js';
+
+/**
+ * Shared transformer logic for all providers.
+ *
+ * @param {Object} config - Provider-specific configuration
+ * @param {string} config.provider - Provider key for placeholders (e.g., 'claude-code')
+ * @param {string} config.displayName - Display name for logging (e.g., 'Claude Code')
+ * @param {string} config.configDir - Dot-directory name (e.g., '.claude')
+ * @param {Function} config.buildFrontmatter - (skill, skillName) => frontmatter object
+ * @param {Function} [config.transformBody] - Optional (body, skill) => transformed body
+ * @param {Array} skills - All skills
+ * @param {string} distDir - Distribution output directory
+ * @param {Object} options - Optional settings (prefix, outputSuffix)
+ */
+export function transformProvider(config, skills, distDir, options = {}) {
+  const { provider, displayName, configDir, buildFrontmatter, transformBody } = config;
+  const { prefix = '', outputSuffix = '' } = options;
+  const providerDir = path.join(distDir, `${provider}${outputSuffix}`);
+  const skillsDir = path.join(providerDir, `${configDir}/skills`);
+
+  cleanDir(providerDir);
+  ensureDir(skillsDir);
+
+  const allSkillNames = skills.map(s => s.name);
+  const commandNames = skills.filter(s => s.userInvokable).map(s => `${prefix}${s.name}`);
+  let refCount = 0;
+  let scriptCount = 0;
+
+  for (const skill of skills) {
+    const skillName = `${prefix}${skill.name}`;
+    const skillDir = path.join(skillsDir, skillName);
+
+    const frontmatterObj = buildFrontmatter(skill, skillName);
+    const frontmatter = generateYamlFrontmatter(frontmatterObj);
+
+    let skillBody = replacePlaceholders(skill.body, provider, commandNames);
+
+    // Replace {{scripts_path}} with provider-aware path to skill's scripts directory
+    const scriptsPath = provider === 'claude-code'
+      ? '${CLAUDE_PLUGIN_ROOT}/scripts'
+      : `${configDir}/skills/${skillName}/scripts`;
+    skillBody = skillBody.replace(/\{\{scripts_path\}\}/g, scriptsPath);
+
+    if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames);
+    if (transformBody) skillBody = transformBody(skillBody, skill);
+
+    const content = `${frontmatter}\n\n${skillBody}`;
+    writeFile(path.join(skillDir, 'SKILL.md'), content);
+
+    // Copy reference files if they exist
+    if (skill.references && skill.references.length > 0) {
+      const refDir = path.join(skillDir, 'reference');
+      ensureDir(refDir);
+      for (const ref of skill.references) {
+        writeFile(
+          path.join(refDir, `${ref.name}.md`),
+          replacePlaceholders(ref.content, provider)
+        );
+        refCount++;
+      }
+    }
+
+    // Copy script files if they exist
+    if (skill.scripts && skill.scripts.length > 0) {
+      const scriptsOutDir = path.join(skillDir, 'scripts');
+      ensureDir(scriptsOutDir);
+      for (const script of skill.scripts) {
+        writeFile(path.join(scriptsOutDir, script.name), script.content);
+        scriptCount++;
+      }
+    }
+  }
+
+  const userInvokableCount = skills.filter(s => s.userInvokable).length;
+  const refInfo = refCount > 0 ? ` (${refCount} reference files)` : '';
+  const scriptInfo = scriptCount > 0 ? ` (${scriptCount} script files)` : '';
+  const prefixInfo = prefix ? ` [${prefix}prefixed]` : '';
+  console.log(`✓ ${displayName}${prefixInfo}: ${skills.length} skills (${userInvokableCount} user-invokable)${refInfo}${scriptInfo}`);
+}

scripts/lib/utils.js 🔗

@@ -0,0 +1,487 @@
+import fs from 'fs';
+import path from 'path';
+
+/**
+ * Parse frontmatter from markdown content
+ * Returns { frontmatter: object, body: string }
+ */
+export function parseFrontmatter(content) {
+  const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
+  const match = content.match(frontmatterRegex);
+
+  if (!match) {
+    return { frontmatter: {}, body: content };
+  }
+
+  const [, frontmatterText, body] = match;
+  const frontmatter = {};
+
+  // Simple YAML parser (handles basic key-value and arrays)
+  const lines = frontmatterText.split('\n');
+  let currentKey = null;
+  let currentArray = null;
+
+  for (const line of lines) {
+    if (!line.trim()) continue;
+
+    // Calculate indent level
+    const leadingSpaces = line.length - line.trimStart().length;
+    const trimmed = line.trim();
+
+    // Array item at level 2 (nested under a key)
+    if (trimmed.startsWith('- ') && leadingSpaces >= 2) {
+      if (currentArray) {
+        if (trimmed.startsWith('- name:')) {
+          // New object in array
+          const obj = {};
+          obj.name = trimmed.slice(7).trim();
+          currentArray.push(obj);
+        } else {
+          // Simple string item in array
+          currentArray.push(trimmed.slice(2));
+        }
+      }
+      continue;
+    }
+
+    // Property of array object (indented further)
+    if (leadingSpaces >= 4 && currentArray && currentArray.length > 0) {
+      const colonIndex = trimmed.indexOf(':');
+      if (colonIndex > 0) {
+        const key = trimmed.slice(0, colonIndex).trim();
+        const value = trimmed.slice(colonIndex + 1).trim();
+        const lastObj = currentArray[currentArray.length - 1];
+        lastObj[key] = value === 'true' ? true : value === 'false' ? false : value;
+      }
+      continue;
+    }
+
+    // Top-level key-value pair
+    if (leadingSpaces === 0) {
+      const colonIndex = trimmed.indexOf(':');
+      if (colonIndex > 0) {
+        const key = trimmed.slice(0, colonIndex).trim();
+        const value = trimmed.slice(colonIndex + 1).trim();
+
+        if (value) {
+          // Strip YAML quotes
+          const unquoted = (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))
+            ? value.slice(1, -1)
+            : value;
+          frontmatter[key] = unquoted === 'true' ? true : unquoted === 'false' ? false : unquoted;
+          currentKey = key;
+          currentArray = null;
+        } else {
+          // Start of array
+          currentKey = key;
+          currentArray = [];
+          frontmatter[key] = currentArray;
+        }
+      }
+    }
+  }
+
+  return { frontmatter, body: body.trim() };
+}
+
+/**
+ * Recursively read all .md files from a directory
+ */
+export function readFilesRecursive(dir, fileList = []) {
+  if (!fs.existsSync(dir)) {
+    return fileList;
+  }
+
+  const files = fs.readdirSync(dir);
+
+  for (const file of files) {
+    const filePath = path.join(dir, file);
+    const stat = fs.statSync(filePath);
+
+    if (stat.isDirectory()) {
+      readFilesRecursive(filePath, fileList);
+    } else if (file.endsWith('.md')) {
+      fileList.push(filePath);
+    }
+  }
+
+  return fileList;
+}
+
+/**
+ * Read and parse all source files (unified skills architecture)
+ * All source lives in source/skills/{name}/SKILL.md
+ * Returns { skills } where each skill has userInvocable flag
+ */
+export function readSourceFiles(rootDir) {
+  const skillsDir = path.join(rootDir, 'source/skills');
+
+  const skills = [];
+
+  if (fs.existsSync(skillsDir)) {
+    const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
+
+    for (const entry of entries) {
+      const entryPath = path.join(skillsDir, entry.name);
+
+      if (entry.isDirectory()) {
+        // Directory-based skill with potential references
+        const skillMdPath = path.join(entryPath, 'SKILL.md');
+        if (fs.existsSync(skillMdPath)) {
+          const content = fs.readFileSync(skillMdPath, 'utf-8');
+          const { frontmatter, body } = parseFrontmatter(content);
+
+          // Read reference files if they exist
+          const references = [];
+          const referenceDir = path.join(entryPath, 'reference');
+          if (fs.existsSync(referenceDir)) {
+            const refFiles = fs.readdirSync(referenceDir).filter(f => f.endsWith('.md'));
+            for (const refFile of refFiles) {
+              const refPath = path.join(referenceDir, refFile);
+              const refContent = fs.readFileSync(refPath, 'utf-8');
+              references.push({
+                name: path.basename(refFile, '.md'),
+                content: refContent,
+                filePath: refPath
+              });
+            }
+          }
+
+          // Read script files if they exist
+          const scripts = [];
+          const scriptsDir = path.join(entryPath, 'scripts');
+          if (fs.existsSync(scriptsDir)) {
+            const scriptFiles = fs.readdirSync(scriptsDir).filter(f => fs.statSync(path.join(scriptsDir, f)).isFile());
+            for (const scriptFile of scriptFiles) {
+              const scriptPath = path.join(scriptsDir, scriptFile);
+              const scriptContent = fs.readFileSync(scriptPath, 'utf-8');
+              scripts.push({
+                name: scriptFile,
+                content: scriptContent,
+                filePath: scriptPath
+              });
+            }
+          }
+
+          skills.push({
+            name: frontmatter.name || entry.name,
+            description: frontmatter.description || '',
+            license: frontmatter.license || '',
+            compatibility: frontmatter.compatibility || '',
+            metadata: frontmatter.metadata || null,
+            allowedTools: frontmatter['allowed-tools'] || '',
+            userInvocable: frontmatter['user-invocable'] === true || frontmatter['user-invocable'] === 'true',
+            argumentHint: frontmatter['argument-hint'] || '',
+            context: frontmatter.context || null,
+            body,
+            filePath: skillMdPath,
+            references,
+            scripts
+          });
+        }
+      }
+    }
+  }
+
+  return { skills };
+}
+
+/**
+ * Ensure directory exists, create if needed
+ */
+export function ensureDir(dirPath) {
+  if (!fs.existsSync(dirPath)) {
+    fs.mkdirSync(dirPath, { recursive: true });
+  }
+}
+
+/**
+ * Clean directory (remove all contents)
+ */
+export function cleanDir(dirPath) {
+  if (fs.existsSync(dirPath)) {
+    fs.rmSync(dirPath, { recursive: true, force: true });
+  }
+}
+
+/**
+ * Write file with automatic directory creation
+ */
+export function writeFile(filePath, content) {
+  const dir = path.dirname(filePath);
+  ensureDir(dir);
+  fs.writeFileSync(filePath, content, 'utf-8');
+}
+
+/**
+ * Extract patterns from frontend-design SKILL.md
+ * Parses DO/DON'T lines grouped by section headings.
+ * Recognizes both formats:
+ *   - Markdown bullet form:  `**DO**: …`  /  `**DON'T**: …`
+ *   - XML-block prose form:  `DO …`       /  `DO NOT …`  (used inside
+ *     <typography_rules>, <color_rules>, <spatial_rules>, <absolute_bans>)
+ * Returns { patterns: [...], antipatterns: [...] }
+ */
+export function readPatterns(rootDir) {
+  const skillPath = path.join(rootDir, 'source/skills/impeccable/SKILL.md');
+
+  if (!fs.existsSync(skillPath)) {
+    return { patterns: [], antipatterns: [] };
+  }
+
+  const content = fs.readFileSync(skillPath, 'utf-8');
+  const lines = content.split('\n');
+
+  const patternsMap = {};  // category -> items[]
+  const antipatternsMap = {};  // category -> items[]
+  let currentSection = null;
+
+  const pushPattern = (item) => {
+    if (!currentSection) return;
+    if (!patternsMap[currentSection]) patternsMap[currentSection] = [];
+    patternsMap[currentSection].push(item);
+  };
+  const pushAntipattern = (item) => {
+    if (!currentSection) return;
+    if (!antipatternsMap[currentSection]) antipatternsMap[currentSection] = [];
+    antipatternsMap[currentSection].push(item);
+  };
+
+  for (const line of lines) {
+    const trimmed = line.trim();
+
+    // Track section headings (### Typography, ### Color & Theme, etc.)
+    if (trimmed.startsWith('### ')) {
+      currentSection = trimmed.slice(4).trim();
+      // Normalize "Color & Theme" to "Color & Contrast" for consistency
+      if (currentSection === 'Color & Theme') {
+        currentSection = 'Color & Contrast';
+      }
+      continue;
+    }
+
+    // Markdown bullet form (legacy): **DO**: ... and **DON'T**: ...
+    if (trimmed.startsWith('**DO**:')) {
+      pushPattern(trimmed.slice(7).trim());
+      continue;
+    }
+    if (trimmed.startsWith("**DON'T**:")) {
+      pushAntipattern(trimmed.slice(10).trim());
+      continue;
+    }
+
+    // XML-block prose form (current). Both space and colon variants:
+    //   "DO NOT use ..."  /  "DO NOT: Use ..."
+    //   "DO use ..."      /  "DO: Use ..."
+    // IMPORTANT: check `DO NOT` BEFORE `DO` so the prefix doesn't get
+    // gobbled by the wrong matcher.
+    if (trimmed.startsWith('DO NOT: ')) {
+      pushAntipattern(trimmed.slice('DO NOT: '.length).trim());
+      continue;
+    }
+    if (trimmed.startsWith('DO NOT ')) {
+      pushAntipattern(trimmed.slice('DO NOT '.length).trim());
+      continue;
+    }
+    if (trimmed.startsWith('DO: ')) {
+      pushPattern(trimmed.slice('DO: '.length).trim());
+      continue;
+    }
+    if (trimmed.startsWith('DO ')) {
+      pushPattern(trimmed.slice('DO '.length).trim());
+      continue;
+    }
+  }
+
+  // Convert maps to arrays in consistent order
+  const sectionOrder = ['Typography', 'Color & Contrast', 'Layout & Space', 'Visual Details', 'Motion', 'Interaction', 'Responsive', 'UX Writing'];
+
+  const patterns = [];
+  const antipatterns = [];
+
+  for (const section of sectionOrder) {
+    if (patternsMap[section] && patternsMap[section].length > 0) {
+      patterns.push({ name: section, items: patternsMap[section] });
+    }
+    if (antipatternsMap[section] && antipatternsMap[section].length > 0) {
+      antipatterns.push({ name: section, items: antipatternsMap[section] });
+    }
+  }
+
+  return { patterns, antipatterns };
+}
+
+/**
+ * Provider-specific placeholders
+ */
+export const PROVIDER_PLACEHOLDERS = {
+  'claude-code': {
+    model: 'Claude',
+    config_file: 'CLAUDE.md',
+    ask_instruction: 'STOP and call the AskUserQuestion tool to clarify.',
+    command_prefix: '/'
+  },
+  'cursor': {
+    model: 'the model',
+    config_file: '.cursorrules',
+    ask_instruction: 'ask the user directly to clarify what you cannot infer.',
+    command_prefix: '/'
+  },
+  'gemini': {
+    model: 'Gemini',
+    config_file: 'GEMINI.md',
+    ask_instruction: 'ask the user directly to clarify what you cannot infer.',
+    command_prefix: '/'
+  },
+  'codex': {
+    model: 'GPT',
+    config_file: 'AGENTS.md',
+    ask_instruction: 'ask the user directly to clarify what you cannot infer.',
+    command_prefix: '$'
+  },
+  'agents': {
+    model: 'the model',
+    config_file: '.github/copilot-instructions.md',
+    ask_instruction: 'ask the user directly to clarify what you cannot infer.',
+    command_prefix: '/'
+  },
+  'kiro': {
+    model: 'Claude',
+    config_file: '.kiro/settings.json',
+    ask_instruction: 'ask the user directly to clarify what you cannot infer.',
+    command_prefix: '/'
+  },
+  opencode: {
+    model: 'Claude',
+    config_file: 'AGENTS.md',
+    ask_instruction: 'STOP and call the `question` tool to clarify.',
+    command_prefix: '/'
+  },
+  'pi': {
+    model: 'the model',
+    config_file: 'AGENTS.md',
+    ask_instruction: 'ask the user directly to clarify what you cannot infer.',
+    command_prefix: '/'
+  },
+  'trae': {
+    model: 'the model',
+    config_file: 'RULES.md',
+    ask_instruction: 'ask the user directly to clarify what you cannot infer.',
+    command_prefix: '/'
+  },
+  'rovo-dev': {
+    model: 'Rovo Dev',
+    config_file: 'AGENTS.md',
+    ask_instruction: 'ask the user directly to clarify what you cannot infer.',
+    command_prefix: '/'
+  }
+};
+
+/**
+ * Replace all {{placeholder}} tokens with provider-specific values
+ */
+/**
+ * Prefix skill cross-references in body text.
+ * Replaces patterns like `/skillname` and `the skillname skill` with prefixed versions.
+ *
+ * @param {string} content - The skill body text
+ * @param {string} prefix - The prefix to add (e.g., 'i-')
+ * @param {string[]} skillNames - Array of all skill names
+ * @param {string} commandPrefix - The command invocation prefix (e.g., '/' or '$')
+ */
+export function prefixSkillReferences(content, prefix, skillNames, commandPrefix = '/') {
+  if (!prefix || !skillNames || skillNames.length === 0) return content;
+
+  let result = content;
+  // Sort by length descending to avoid partial matches (e.g. 'teach-impeccable' before 'teach')
+  const sorted = [...skillNames].sort((a, b) => b.length - a.length);
+
+  for (const name of sorted) {
+    const prefixed = `${prefix}${name}`;
+
+    // Replace command invocations (e.g., `/skillname` or `$skillname`) with prefixed versions
+    const escapedPrefix = escapeRegex(commandPrefix);
+    result = result.replace(
+      new RegExp(`${escapedPrefix}(?=${escapeRegex(name)}(?:[^a-zA-Z0-9_-]|$))`, 'g'),
+      `${commandPrefix}${prefix}`
+    );
+
+    // Replace `the skillname skill` references
+    result = result.replace(
+      new RegExp(`(the) ${escapeRegex(name)} skill`, 'gi'),
+      (_, article) => `${article} ${prefixed} skill`
+    );
+  }
+
+  return result;
+}
+
+function escapeRegex(str) {
+  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
+const EXCLUDED_FROM_SUGGESTIONS = new Set([
+  'impeccable', 'i-impeccable',               // foundational skill, not a steering command
+  'teach-impeccable', 'i-teach-impeccable',    // deprecated shim
+  'frontend-design', 'i-frontend-design',      // deprecated shim
+]);
+
+export function replacePlaceholders(content, provider, commandNames = [], allSkillNames = []) {
+  const placeholders = PROVIDER_PLACEHOLDERS[provider] || PROVIDER_PLACEHOLDERS['cursor'];
+  const cmdPrefix = placeholders.command_prefix || '/';
+  const commandList = commandNames
+    .filter(n => !EXCLUDED_FROM_SUGGESTIONS.has(n))
+    .map(n => `${cmdPrefix}${n}`)
+    .join(', ');
+
+  let result = content
+    .replace(/\{\{model\}\}/g, placeholders.model)
+    .replace(/\{\{config_file\}\}/g, placeholders.config_file)
+    .replace(/\{\{ask_instruction\}\}/g, placeholders.ask_instruction)
+    .replace(/\{\{command_prefix\}\}/g, cmdPrefix)
+    .replace(/\{\{available_commands\}\}/g, commandList);
+
+  // Replace `/skillname` invocations with the correct command prefix for this provider
+  // (e.g., `/normalize` → `$normalize` for Codex)
+  if (cmdPrefix !== '/' && allSkillNames.length > 0) {
+    const sorted = [...allSkillNames].sort((a, b) => b.length - a.length);
+    for (const name of sorted) {
+      result = result.replace(
+        new RegExp(`\\/(?=${escapeRegex(name)}(?:[^a-zA-Z0-9_-]|$))`, 'g'),
+        cmdPrefix
+      );
+    }
+  }
+
+  return result;
+}
+
+/**
+ * Generate YAML frontmatter string
+ */
+export function generateYamlFrontmatter(data) {
+  const lines = ['---'];
+
+  for (const [key, value] of Object.entries(data)) {
+    if (Array.isArray(value)) {
+      lines.push(`${key}:`);
+      for (const item of value) {
+        if (typeof item === 'object') {
+          lines.push(`  - name: ${item.name}`);
+          if (item.description) lines.push(`    description: ${item.description}`);
+          if (item.required !== undefined) lines.push(`    required: ${item.required}`);
+        } else {
+          lines.push(`  - ${item}`);
+        }
+      }
+    } else if (typeof value === 'boolean') {
+      lines.push(`${key}: ${value}`);
+    } else {
+      const needsQuoting = typeof value === 'string' && /^[\[{]/.test(value);
+      lines.push(`${key}: ${needsQuoting ? `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` : value}`);
+    }
+  }
+
+  lines.push('---');
+  return lines.join('\n');
+}

scripts/lib/zip.js 🔗

@@ -0,0 +1,62 @@
+/**
+ * ZIP Generation Utilities
+ *
+ * Creates ZIP bundles for each provider's distribution
+ * Uses archiver instead of shell `zip` for cross-platform compatibility
+ * (Cloudflare Pages build environment may not have zip installed)
+ */
+
+import path from 'path';
+import { createWriteStream, existsSync, statSync } from 'fs';
+import archiver from 'archiver';
+
+/**
+ * Create ZIP file for a provider directory
+ * @param {string} providerDir - Path to provider directory
+ * @param {string} distDir - Path to dist directory
+ * @param {string} providerName - Name of the provider
+ */
+export async function createProviderZip(providerDir, distDir, providerName) {
+  const zipFileName = `${providerName}.zip`;
+  const zipPath = path.join(distDir, zipFileName);
+
+  if (!existsSync(providerDir)) {
+    console.warn(`⚠️  Provider directory not found: ${providerDir}`);
+    return;
+  }
+
+  try {
+    await new Promise((resolve, reject) => {
+      const output = createWriteStream(zipPath);
+      const archive = archiver('zip', { zlib: { level: 9 } });
+
+      output.on('close', resolve);
+      archive.on('error', reject);
+
+      archive.pipe(output);
+      archive.glob('**/*', {
+        cwd: providerDir,
+        dot: true,
+        ignore: ['**/.DS_Store'],
+      });
+      archive.finalize();
+    });
+
+    const stats = statSync(zipPath);
+    const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
+    console.log(`  📦 ${zipFileName} (${sizeMB} MB)`);
+  } catch (error) {
+    console.error(`  ❌ Failed to create ${zipFileName}:`, error.message);
+  }
+}
+
+/**
+ * Create ZIP files for all providers + universal
+ * @param {string} distDir - Path to dist directory
+ */
+export async function createAllZips(distDir) {
+  console.log('\n📦 Creating ZIP bundles...');
+
+  await createProviderZip(path.join(distDir, 'universal'), distDir, 'universal');
+  await createProviderZip(path.join(distDir, 'universal-prefixed'), distDir, 'universal-prefixed');
+}

scripts/screenshot-antipatterns.js 🔗

@@ -0,0 +1,102 @@
+#!/usr/bin/env node
+
+/**
+ * Screenshot Anti-Pattern Examples
+ *
+ * Takes 1080x1080 screenshots of each anti-pattern example for LinkedIn sharing.
+ * Requires the dev server to be running on localhost:3000
+ */
+
+import { chromium } from 'playwright';
+import path from 'path';
+import fs from 'fs';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const ROOT_DIR = path.resolve(__dirname, '..');
+const EXAMPLES_DIR = path.join(ROOT_DIR, 'public', 'antipattern-examples');
+const OUTPUT_DIR = path.join(ROOT_DIR, 'public', 'antipattern-images');
+
+async function screenshotAntipatterns() {
+  // Ensure output directory exists
+  if (!fs.existsSync(OUTPUT_DIR)) {
+    fs.mkdirSync(OUTPUT_DIR, { recursive: true });
+  }
+
+  // Get all HTML files in the examples directory
+  const files = fs.readdirSync(EXAMPLES_DIR)
+    .filter(f => f.endsWith('.html'));
+
+  if (files.length === 0) {
+    console.log('No HTML files found in', EXAMPLES_DIR);
+    return;
+  }
+
+  console.log(`📸 Taking screenshots of ${files.length} anti-pattern example(s)...\n`);
+
+  const browser = await chromium.launch();
+  const context = await browser.newContext({
+    viewport: { width: 1200, height: 1200 },
+    deviceScaleFactor: 2, // 2x for high-res output
+  });
+
+  for (const file of files) {
+    const name = path.basename(file, '.html');
+    const url = `http://localhost:3000/antipattern-examples/${file}`;
+    const outputPath = path.join(OUTPUT_DIR, `${name}.png`);
+
+    console.log(`  ${name}...`);
+
+    const page = await context.newPage();
+    await page.goto(url, { waitUntil: 'networkidle' });
+
+    // Wait for fonts to load
+    await page.waitForTimeout(500);
+
+    // Screenshot the .container element (1080x1080)
+    const container = await page.$('.container');
+    if (container) {
+      await container.screenshot({
+        path: outputPath,
+        type: 'png',
+      });
+      console.log(`    ✓ Saved to ${path.relative(ROOT_DIR, outputPath)}`);
+    } else {
+      // Fallback: screenshot full page cropped
+      await page.screenshot({
+        path: outputPath,
+        type: 'png',
+        clip: { x: 0, y: 0, width: 1080, height: 1080 },
+      });
+      console.log(`    ✓ Saved (full page crop) to ${path.relative(ROOT_DIR, outputPath)}`);
+    }
+
+    await page.close();
+  }
+
+  await browser.close();
+  console.log(`\n✨ Done! Screenshots saved to ${path.relative(ROOT_DIR, OUTPUT_DIR)}/`);
+}
+
+// Check if dev server is running
+async function checkServer() {
+  try {
+    const response = await fetch('http://localhost:3000');
+    return response.ok;
+  } catch {
+    return false;
+  }
+}
+
+async function main() {
+  const serverRunning = await checkServer();
+  if (!serverRunning) {
+    console.error('❌ Dev server not running. Please start it with: bun run dev');
+    process.exit(1);
+  }
+
+  await screenshotAntipatterns();
+}
+
+main().catch(console.error);

server/index.js 🔗

@@ -0,0 +1,210 @@
+import { serve, file } from "bun";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+import homepage from "../public/index.html";
+import cheatsheet from "../public/cheatsheet.html";
+import gallery from "../public/gallery.html";
+import privacy from "../public/privacy.html";
+import {
+  getSkills,
+  getCommands,
+  getCommandSource,
+  getPatterns,
+  handleFileDownload,
+  handleBundleDownload
+} from "./lib/api-handlers.js";
+import { generateSubPages } from "../scripts/build-sub-pages.js";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const ROOT_DIR = path.resolve(__dirname, "..");
+
+// Pre-generate sub-pages so dev + prod share the same output shape.
+console.log("📝 Generating sub-pages for dev server...");
+const { files: subPageFiles } = await generateSubPages(ROOT_DIR);
+console.log(`✓ Generated ${subPageFiles.length} sub-page(s)`);
+
+// Helper: serve a generated HTML file by absolute path, 404 if missing.
+async function serveGenerated(pagePath) {
+  const f = file(pagePath);
+  if (!(await f.exists())) return new Response("Not Found", { status: 404 });
+  return new Response(f, {
+    headers: {
+      "Content-Type": "text/html;charset=utf-8",
+      "X-Content-Type-Options": "nosniff",
+      "X-Frame-Options": "DENY",
+    },
+  });
+}
+
+const server = serve({
+  port: process.env.PORT || 3000,
+
+  routes: {
+    "/": homepage,
+    "/cheatsheet": cheatsheet,
+    "/gallery": gallery,
+    "/privacy": privacy,
+
+    // Generated sub-pages — served directly from the pre-generated files
+    "/skills": () => serveGenerated(path.join(ROOT_DIR, "public/skills/index.html")),
+    "/skills/:id": (req) => {
+      const id = req.params.id.replace(/[^a-z0-9-]/gi, "");
+      return serveGenerated(path.join(ROOT_DIR, `public/skills/${id}.html`));
+    },
+    "/anti-patterns": () => serveGenerated(path.join(ROOT_DIR, "public/anti-patterns/index.html")),
+    "/visual-mode": () => serveGenerated(path.join(ROOT_DIR, "public/visual-mode/index.html")),
+    "/tutorials": () => serveGenerated(path.join(ROOT_DIR, "public/tutorials/index.html")),
+    "/tutorials/:slug": (req) => {
+      const slug = req.params.slug.replace(/[^a-z0-9-]/gi, "");
+      return serveGenerated(path.join(ROOT_DIR, `public/tutorials/${slug}.html`));
+    },
+
+    // Static assets - all public subdirectories
+    "/assets/*": async (req) => {
+      const url = new URL(req.url);
+      if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 });
+      const filePath = `./public${url.pathname}`;
+      const assetFile = file(filePath);
+      if (await assetFile.exists()) {
+        return new Response(assetFile, {
+          headers: { "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY" }
+        });
+      }
+      return new Response("Not Found", { status: 404 });
+    },
+    "/css/*": async (req) => {
+      const url = new URL(req.url);
+      if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 });
+      const filePath = `./public${url.pathname}`;
+      const assetFile = file(filePath);
+      if (await assetFile.exists()) {
+        return new Response(assetFile, {
+          headers: { "Content-Type": "text/css", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY" }
+        });
+      }
+      return new Response("Not Found", { status: 404 });
+    },
+    "/js/*": async (req) => {
+      const url = new URL(req.url);
+      if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 });
+      // Check public/js/ first, then fall back to built artifacts
+      const headers = { "Content-Type": "application/javascript", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY" };
+      const publicFile = file(`./public${url.pathname}`);
+      if (await publicFile.exists()) return new Response(publicFile, { headers });
+      // Browser detector served from impeccable package
+      if (url.pathname === '/js/detect-antipatterns-browser.js') {
+        const pkgFile = file('./src/detect-antipatterns-browser.js');
+        if (await pkgFile.exists()) return new Response(pkgFile, { headers });
+      }
+      return new Response("Not Found", { status: 404 });
+    },
+    // Test fixtures (for browser visual testing)
+    "/fixtures/*": async (req) => {
+      const url = new URL(req.url);
+      if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 });
+      const filePath = `./tests${url.pathname}`;
+      const assetFile = file(filePath);
+      if (await assetFile.exists()) {
+        const ext = url.pathname.split('.').pop();
+        const types = { html: 'text/html', css: 'text/css', js: 'application/javascript' };
+        return new Response(assetFile, {
+          headers: { "Content-Type": types[ext] || "application/octet-stream", "X-Content-Type-Options": "nosniff" }
+        });
+      }
+      return new Response("Not Found", { status: 404 });
+    },
+    "/antipattern-images/*": async (req) => {
+      const url = new URL(req.url);
+      if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 });
+      const filePath = `./public${url.pathname}`;
+      const assetFile = file(filePath);
+      if (await assetFile.exists()) {
+        return new Response(assetFile, {
+          headers: { "X-Content-Type-Options": "nosniff" }
+        });
+      }
+      return new Response("Not Found", { status: 404 });
+    },
+    "/antipattern-examples/*": async (req) => {
+      const url = new URL(req.url);
+      if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 });
+      const filePath = `./public${url.pathname}`;
+      const assetFile = file(filePath);
+      if (await assetFile.exists()) {
+        return new Response(assetFile, {
+          headers: { "Content-Type": "text/html", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "SAMEORIGIN" }
+        });
+      }
+      return new Response("Not Found", { status: 404 });
+    },
+
+    // API: Get all skills
+    "/api/skills": {
+      async GET() {
+        const skills = await getSkills();
+        return Response.json(skills);
+      },
+    },
+    
+    // API: Get all commands
+    "/api/commands": {
+      async GET() {
+        const commands = await getCommands();
+        return Response.json(commands);
+      },
+    },
+
+    // API: Get patterns and antipatterns
+    "/api/patterns": {
+      async GET() {
+        const patterns = await getPatterns();
+        return Response.json(patterns);
+      },
+    },
+
+    // API: Get command source content
+    "/api/command-source/:id": async (req) => {
+      const { id } = req.params;
+      const result = await getCommandSource(id);
+      if (result && result.error) {
+        return Response.json({ error: result.error }, { status: result.status });
+      }
+      if (!result) {
+        return Response.json({ error: "Command not found" }, { status: 404 });
+      }
+      return Response.json({ content: result });
+    },
+
+    // API: Download individual file
+    "/api/download/:type/:provider/:id": async (req) => {
+      const { type, provider, id } = req.params;
+      return handleFileDownload(type, provider, id);
+    },
+    
+    // API: Download provider bundle ZIP
+    "/api/download/bundle/:provider": async (req) => {
+      const { provider } = req.params;
+      return handleBundleDownload(provider);
+    },
+  },
+  
+  // Serve root-level static files (og-image.png, favicon, robots.txt, etc.)
+  fetch(req) {
+    const url = new URL(req.url);
+    if (url.pathname.includes('..')) {
+      return new Response("Bad Request", { status: 400 });
+    }
+    const filePath = `./public${url.pathname}`;
+    const staticFile = file(filePath);
+    if (staticFile.size > 0) {
+      return new Response(staticFile);
+    }
+    return new Response("Not Found", { status: 404 });
+  },
+
+  development: process.env.NODE_ENV !== "production",
+});
+
+console.log(`🎨 impeccable.style running at ${server.url}`);
+

server/lib/api-handlers.js 🔗

@@ -0,0 +1,165 @@
+import { readdir, readFile } from "fs/promises";
+import { basename, join, dirname } from "path";
+import { existsSync } from "fs";
+import { fileURLToPath } from "url";
+import { readPatterns, parseFrontmatter } from "../../scripts/lib/utils.js";
+import { FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS } from "../../lib/download-providers.js";
+import {
+	isAllowedBundleProvider,
+	isAllowedFileProvider,
+	isAllowedType,
+	isValidId,
+	sanitizeFilename
+} from "./validation.js";
+
+// Get project root directory (works in both Node.js and Bun, including Vercel)
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+const PROJECT_ROOT = join(__dirname, "..", "..");
+
+// Helper to read file content (works in both Node.js and Bun)
+async function readFileContent(filePath) {
+	return readFile(filePath, "utf-8");
+}
+
+// Read all skills from source/skills/ subdirectories
+export async function getSkills() {
+	const skillsDir = join(PROJECT_ROOT, "source", "skills");
+	const entries = await readdir(skillsDir, { withFileTypes: true });
+	const skills = [];
+
+	for (const entry of entries) {
+		if (!entry.isDirectory()) continue;
+		const skillMdPath = join(skillsDir, entry.name, "SKILL.md");
+		if (!existsSync(skillMdPath)) continue;
+
+		const content = await readFileContent(skillMdPath);
+		const { frontmatter } = parseFrontmatter(content);
+
+		skills.push({
+			id: entry.name,
+			name: frontmatter.name || entry.name,
+			description: frontmatter.description || "No description available",
+			userInvocable: frontmatter['user-invocable'] === true || frontmatter['user-invocable'] === 'true',
+		});
+	}
+
+	return skills;
+}
+
+// Read commands (user-invocable skills)
+export async function getCommands() {
+	const allSkills = await getSkills();
+	return allSkills.filter(s => s.userInvocable);
+}
+
+// Get command/skill source content
+export async function getCommandSource(id) {
+	if (!isValidId(id)) {
+		return { error: "Invalid command ID", status: 400 };
+	}
+
+	const skillPath = join(PROJECT_ROOT, "source", "skills", id, "SKILL.md");
+
+	try {
+		if (!existsSync(skillPath)) {
+			return null;
+		}
+		const content = await readFileContent(skillPath);
+		return content;
+	} catch (error) {
+		console.error("Error reading skill source:", error);
+		return null;
+	}
+}
+
+// Get the appropriate file path for a provider
+export function getFilePath(type, provider, id) {
+	const distDir = join(PROJECT_ROOT, "dist");
+	const configDir = FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS[provider];
+	if (!configDir) return null;
+
+	// Everything is a skill now
+	if (type === "skill" || type === "command") {
+		return join(distDir, provider, configDir, "skills", id, "SKILL.md");
+	}
+
+	return null;
+}
+
+// Handle individual file download
+export async function handleFileDownload(type, provider, id) {
+	if (!isAllowedType(type)) {
+		return new Response("Invalid type", { status: 400 });
+	}
+
+	if (!isAllowedFileProvider(provider)) {
+		return new Response("Invalid provider", { status: 400 });
+	}
+
+	if (!isValidId(id)) {
+		return new Response("Invalid file ID", { status: 400 });
+	}
+
+	const filePath = getFilePath(type, provider, id);
+
+	if (!filePath) {
+		return new Response("Invalid provider", { status: 400 });
+	}
+
+	try {
+		if (!existsSync(filePath)) {
+			return new Response("File not found", { status: 404 });
+		}
+
+		const content = await readFile(filePath);
+		const fileName = sanitizeFilename(basename(filePath));
+		return new Response(content, {
+			headers: {
+				"Content-Type": "application/octet-stream",
+				"Content-Disposition": `attachment; filename="${fileName}"`,
+			},
+		});
+	} catch (error) {
+		console.error("Error downloading file:", error);
+		return new Response("Error downloading file", { status: 500 });
+	}
+}
+
+// Extract patterns from SKILL.md using the shared utility
+export async function getPatterns() {
+	try {
+		return readPatterns(PROJECT_ROOT);
+	} catch (error) {
+		console.error("Error reading patterns:", error);
+		return { patterns: [], antipatterns: [] };
+	}
+}
+
+// Handle bundle download
+export async function handleBundleDownload(provider) {
+	if (!isAllowedBundleProvider(provider)) {
+		return new Response("Invalid provider", { status: 400 });
+	}
+
+	const distDir = join(PROJECT_ROOT, "dist");
+	const zipPath = join(distDir, `${provider}.zip`);
+
+	try {
+		if (!existsSync(zipPath)) {
+			return new Response("Bundle not found", { status: 404 });
+		}
+
+		const content = await readFile(zipPath);
+		const safeProvider = sanitizeFilename(provider);
+		return new Response(content, {
+			headers: {
+				"Content-Type": "application/zip",
+				"Content-Disposition": `attachment; filename="impeccable-style-${safeProvider}.zip"`,
+			},
+		});
+	} catch (error) {
+		console.error("Error downloading bundle:", error);
+		return new Response("Error downloading bundle", { status: 500 });
+	}
+}

server/lib/validation.js 🔗

@@ -0,0 +1,40 @@
+// Shared validation helpers for input sanitization
+
+import {
+  BUNDLE_DOWNLOAD_PROVIDERS,
+  DOWNLOAD_PROVIDERS,
+  FILE_DOWNLOAD_PROVIDERS,
+} from '../../lib/download-providers.js';
+
+// Only allow alphanumeric, hyphens, and underscores in IDs
+export const VALID_ID = /^[a-zA-Z0-9_-]+$/;
+
+export const ALLOWED_PROVIDERS = DOWNLOAD_PROVIDERS;
+export const ALLOWED_FILE_PROVIDERS = FILE_DOWNLOAD_PROVIDERS;
+export const ALLOWED_BUNDLE_PROVIDERS = BUNDLE_DOWNLOAD_PROVIDERS;
+export const ALLOWED_TYPES = ['skill', 'command'];
+
+export function isValidId(id) {
+  return typeof id === 'string' && VALID_ID.test(id);
+}
+
+export function isAllowedProvider(provider) {
+  return ALLOWED_PROVIDERS.includes(provider);
+}
+
+export function isAllowedFileProvider(provider) {
+  return ALLOWED_FILE_PROVIDERS.includes(provider);
+}
+
+export function isAllowedBundleProvider(provider) {
+  return ALLOWED_BUNDLE_PROVIDERS.includes(provider);
+}
+
+export function isAllowedType(type) {
+  return ALLOWED_TYPES.includes(type);
+}
+
+// Sanitize a filename for use in Content-Disposition headers
+export function sanitizeFilename(filename) {
+  return filename.replace(/[^a-zA-Z0-9._-]/g, '');
+}

skills-lock.json 🔗

@@ -0,0 +1,110 @@
+{
+  "version": 1,
+  "skills": {
+    "adapt": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "fb7cca7602be381b7b486e731fdcd480335cbb9b925e1911276f5bb9352c6b2f"
+    },
+    "animate": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "b00cb71343fa7e987489ad330e3dd3e504ff893b9ddd2b30cacea93691b78e46"
+    },
+    "arrange": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "698fb952e9ef0d2551a5c3421ef61e084934420e0d8371a02efb4f76f21049e8"
+    },
+    "audit": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "eb9109e542194f8711cffce23095ef5da2deb4ed0b23f59c07e912e3931f1e17"
+    },
+    "bolder": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "4b03286f1dc45da3e1a76a71c0c62729b4887c814dff3084345889cb4780a3a2"
+    },
+    "clarify": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "1138379fb9f10b911094fb57b5607de8efbd77d2467fba4981ccc1c155457c6f"
+    },
+    "colorize": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "6e292a0ba428d339615e025cab1f2692b2e6267ba247574a00545b1dac1acc5a"
+    },
+    "critique": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "59d3facaee0d7c969f23e938c35239183d3fdd9befff526908585e55afa3a9dd"
+    },
+    "delight": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "80127e9c695f768f5df70b9f62ad51cdfb3b4892791c4d901214b08f3be8d37e"
+    },
+    "distill": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "ce6fbd844488a326208c1302c73b2865fa4fb20e447b6a06c038315444d6e0c5"
+    },
+    "extract": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "1bbe30b5be73a86971f6bccb37daae84aaafceaa6d23429d6a3a0442378ac4ec"
+    },
+    "frontend-design": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "9ebe6c652743fcde8d2ae773f34b6f548dcc8b2e75a3a30936adcd8b95dd2d16"
+    },
+    "harden": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "f8ce420b3c78b90707704122264da76514c793ac0e12fb9540ace68d257c231c"
+    },
+    "normalize": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "281b7f9e590e252a6aec3f5f83c5ca548c91e8bccb3fd6eee4cc7d5be0becb4d"
+    },
+    "onboard": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "1cbedf70f906150b1b8bb70b61393eaf67e2e1311e1b5e43bde3e47411bcaa2b"
+    },
+    "optimize": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "f7adc2a2e540a590978b567d82985062bfada83385f5ad5d50b13a127cdc96d6"
+    },
+    "overdrive": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "3b940d458ef13afcb93ccb51ff430510aca4a9733d68fbdc335927a2636a091d"
+    },
+    "polish": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "33a8b5195481719e954b71762de56faaabc68c479602673894e0d0e47336756b"
+    },
+    "quieter": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "6066e73875e4770e624641355cc04662ce75d2b1d1a3673e24dcbd0ec8936297"
+    },
+    "teach-impeccable": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "b3b5541bb9b0a260af793c7d79c2db4a436c2cd9384be34a4024c7f28af72e62"
+    },
+    "typeset": {
+      "source": "pbakaus/impeccable",
+      "sourceType": "github",
+      "computedHash": "0259de15644a89550cfd6a936a8e1866dcb35d20ccc96197614459233a9bcdb3"
+    }
+  }
+}

source/skills/adapt/SKILL.md 🔗

@@ -0,0 +1,199 @@
+---
+name: adapt
+description: "Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility."
+argument-hint: "[target] [context (mobile, tablet, print...)]"
+user-invocable: true
+---
+
+Adapt existing designs to work effectively across different contexts - different screen sizes, devices, platforms, or use cases.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first. Additionally gather: target platforms/devices and usage contexts.
+
+---
+
+## Assess Adaptation Challenge
+
+Understand what needs adaptation and why:
+
+1. **Identify the source context**:
+   - What was it designed for originally? (Desktop web? Mobile app?)
+   - What assumptions were made? (Large screen? Mouse input? Fast connection?)
+   - What works well in current context?
+
+2. **Understand target context**:
+   - **Device**: Mobile, tablet, desktop, TV, watch, print?
+   - **Input method**: Touch, mouse, keyboard, voice, gamepad?
+   - **Screen constraints**: Size, resolution, orientation?
+   - **Connection**: Fast wifi, slow 3G, offline?
+   - **Usage context**: On-the-go vs desk, quick glance vs focused reading?
+   - **User expectations**: What do users expect on this platform?
+
+3. **Identify adaptation challenges**:
+   - What won't fit? (Content, navigation, features)
+   - What won't work? (Hover states on touch, tiny touch targets)
+   - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop)
+
+**CRITICAL**: Adaptation is not just scaling - it's rethinking the experience for the new context.
+
+## Plan Adaptation Strategy
+
+Create context-appropriate strategy:
+
+### Mobile Adaptation (Desktop → Mobile)
+
+**Layout Strategy**:
+- Single column instead of multi-column
+- Vertical stacking instead of side-by-side
+- Full-width components instead of fixed widths
+- Bottom navigation instead of top/side navigation
+
+**Interaction Strategy**:
+- Touch targets 44x44px minimum (not hover-dependent)
+- Swipe gestures where appropriate (lists, carousels)
+- Bottom sheets instead of dropdowns
+- Thumbs-first design (controls within thumb reach)
+- Larger tap areas with more spacing
+
+**Content Strategy**:
+- Progressive disclosure (don't show everything at once)
+- Prioritize primary content (secondary content in tabs/accordions)
+- Shorter text (more concise)
+- Larger text (16px minimum)
+
+**Navigation Strategy**:
+- Hamburger menu or bottom navigation
+- Reduce navigation complexity
+- Sticky headers for context
+- Back button in navigation flow
+
+### Tablet Adaptation (Hybrid Approach)
+
+**Layout Strategy**:
+- Two-column layouts (not single or three-column)
+- Side panels for secondary content
+- Master-detail views (list + detail)
+- Adaptive based on orientation (portrait vs landscape)
+
+**Interaction Strategy**:
+- Support both touch and pointer
+- Touch targets 44x44px but allow denser layouts than phone
+- Side navigation drawers
+- Multi-column forms where appropriate
+
+### Desktop Adaptation (Mobile → Desktop)
+
+**Layout Strategy**:
+- Multi-column layouts (use horizontal space)
+- Side navigation always visible
+- Multiple information panels simultaneously
+- Fixed widths with max-width constraints (don't stretch to 4K)
+
+**Interaction Strategy**:
+- Hover states for additional information
+- Keyboard shortcuts
+- Right-click context menus
+- Drag and drop where helpful
+- Multi-select with Shift/Cmd
+
+**Content Strategy**:
+- Show more information upfront (less progressive disclosure)
+- Data tables with many columns
+- Richer visualizations
+- More detailed descriptions
+
+### Print Adaptation (Screen → Print)
+
+**Layout Strategy**:
+- Page breaks at logical points
+- Remove navigation, footer, interactive elements
+- Black and white (or limited color)
+- Proper margins for binding
+
+**Content Strategy**:
+- Expand shortened content (show full URLs, hidden sections)
+- Add page numbers, headers, footers
+- Include metadata (print date, page title)
+- Convert charts to print-friendly versions
+
+### Email Adaptation (Web → Email)
+
+**Layout Strategy**:
+- Narrow width (600px max)
+- Single column only
+- Inline CSS (no external stylesheets)
+- Table-based layouts (for email client compatibility)
+
+**Interaction Strategy**:
+- Large, obvious CTAs (buttons not text links)
+- No hover states (not reliable)
+- Deep links to web app for complex interactions
+
+## Implement Adaptations
+
+Apply changes systematically:
+
+### Responsive Breakpoints
+
+Choose appropriate breakpoints:
+- Mobile: 320px-767px
+- Tablet: 768px-1023px
+- Desktop: 1024px+
+- Or content-driven breakpoints (where design breaks)
+
+### Layout Adaptation Techniques
+
+- **CSS Grid/Flexbox**: Reflow layouts automatically
+- **Container Queries**: Adapt based on container, not viewport
+- **`clamp()`**: Fluid sizing between min and max
+- **Media queries**: Different styles for different contexts
+- **Display properties**: Show/hide elements per context
+
+### Touch Adaptation
+
+- Increase touch target sizes (44x44px minimum)
+- Add more spacing between interactive elements
+- Remove hover-dependent interactions
+- Add touch feedback (ripples, highlights)
+- Consider thumb zones (easier to reach bottom than top)
+
+### Content Adaptation
+
+- Use `display: none` sparingly (still downloads)
+- Progressive enhancement (core content first, enhancements on larger screens)
+- Lazy loading for off-screen content
+- Responsive images (`srcset`, `picture` element)
+
+### Navigation Adaptation
+
+- Transform complex nav to hamburger/drawer on mobile
+- Bottom nav bar for mobile apps
+- Persistent side navigation on desktop
+- Breadcrumbs on smaller screens for context
+
+**IMPORTANT**: Test on real devices, not just browser DevTools. Device emulation is helpful but not perfect.
+
+**NEVER**:
+- Hide core functionality on mobile (if it matters, make it work)
+- Assume desktop = powerful device (consider accessibility, older machines)
+- Use different information architecture across contexts (confusing)
+- Break user expectations for platform (mobile users expect mobile patterns)
+- Forget landscape orientation on mobile/tablet
+- Use generic breakpoints blindly (use content-driven breakpoints)
+- Ignore touch on desktop (many desktop devices have touch)
+
+## Verify Adaptations
+
+Test thoroughly across contexts:
+
+- **Real devices**: Test on actual phones, tablets, desktops
+- **Different orientations**: Portrait and landscape
+- **Different browsers**: Safari, Chrome, Firefox, Edge
+- **Different OS**: iOS, Android, Windows, macOS
+- **Different input methods**: Touch, mouse, keyboard
+- **Edge cases**: Very small screens (320px), very large screens (4K)
+- **Slow connections**: Test on throttled network
+
+Remember: You're a cross-platform design expert. Make experiences that feel native to each context while maintaining brand and functionality consistency. Adapt intentionally, test thoroughly.
+

source/skills/animate/SKILL.md 🔗

@@ -0,0 +1,174 @@
+---
+name: animate
+description: "Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Analyze a feature and strategically add animations and micro-interactions that enhance understanding, provide feedback, and create delight.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first. Additionally gather: performance constraints.
+
+---
+
+## Assess Animation Opportunities
+
+Analyze where motion would improve the experience:
+
+1. **Identify static areas**:
+   - **Missing feedback**: Actions without visual acknowledgment (button clicks, form submission, etc.)
+   - **Jarring transitions**: Instant state changes that feel abrupt (show/hide, page loads, route changes)
+   - **Unclear relationships**: Spatial or hierarchical relationships that aren't obvious
+   - **Lack of delight**: Functional but joyless interactions
+   - **Missed guidance**: Opportunities to direct attention or explain behavior
+
+2. **Understand the context**:
+   - What's the personality? (Playful vs serious, energetic vs calm)
+   - What's the performance budget? (Mobile-first? Complex page?)
+   - Who's the audience? (Motion-sensitive users? Power users who want speed?)
+   - What matters most? (One hero animation vs many micro-interactions?)
+
+If any of these are unclear from the codebase, {{ask_instruction}}
+
+**CRITICAL**: Respect `prefers-reduced-motion`. Always provide non-animated alternatives for users who need them.
+
+## Plan Animation Strategy
+
+Create a purposeful animation plan:
+
+- **Hero moment**: What's the ONE signature animation? (Page load? Hero section? Key interaction?)
+- **Feedback layer**: Which interactions need acknowledgment?
+- **Transition layer**: Which state changes need smoothing?
+- **Delight layer**: Where can we surprise and delight?
+
+**IMPORTANT**: One well-orchestrated experience beats scattered animations everywhere. Focus on high-impact moments.
+
+## Implement Animations
+
+Add motion systematically across these categories:
+
+### Entrance Animations
+- **Page load choreography**: Stagger element reveals (100-150ms delays), fade + slide combinations
+- **Hero section**: Dramatic entrance for primary content (scale, parallax, or creative effects)
+- **Content reveals**: Scroll-triggered animations using intersection observer
+- **Modal/drawer entry**: Smooth slide + fade, backdrop fade, focus management
+
+### Micro-interactions
+- **Button feedback**:
+  - Hover: Subtle scale (1.02-1.05), color shift, shadow increase
+  - Click: Quick scale down then up (0.95 → 1), ripple effect
+  - Loading: Spinner or pulse state
+- **Form interactions**:
+  - Input focus: Border color transition, slight scale or glow
+  - Validation: Shake on error, check mark on success, smooth color transitions
+- **Toggle switches**: Smooth slide + color transition (200-300ms)
+- **Checkboxes/radio**: Check mark animation, ripple effect
+- **Like/favorite**: Scale + rotation, particle effects, color transition
+
+### State Transitions
+- **Show/hide**: Fade + slide (not instant), appropriate timing (200-300ms)
+- **Expand/collapse**: Height transition with overflow handling, icon rotation
+- **Loading states**: Skeleton screen fades, spinner animations, progress bars
+- **Success/error**: Color transitions, icon animations, gentle scale pulse
+- **Enable/disable**: Opacity transitions, cursor changes
+
+### Navigation & Flow
+- **Page transitions**: Crossfade between routes, shared element transitions
+- **Tab switching**: Slide indicator, content fade/slide
+- **Carousel/slider**: Smooth transforms, snap points, momentum
+- **Scroll effects**: Parallax layers, sticky headers with state changes, scroll progress indicators
+
+### Feedback & Guidance
+- **Hover hints**: Tooltip fade-ins, cursor changes, element highlights
+- **Drag & drop**: Lift effect (shadow + scale), drop zone highlights, smooth repositioning
+- **Copy/paste**: Brief highlight flash on paste, "copied" confirmation
+- **Focus flow**: Highlight path through form or workflow
+
+### Delight Moments
+- **Empty states**: Subtle floating animations on illustrations
+- **Completed actions**: Confetti, check mark flourish, success celebrations
+- **Easter eggs**: Hidden interactions for discovery
+- **Contextual animation**: Weather effects, time-of-day themes, seasonal touches
+
+## Technical Implementation
+
+Use appropriate techniques for each animation:
+
+### Timing & Easing
+
+**Durations by purpose:**
+- **100-150ms**: Instant feedback (button press, toggle)
+- **200-300ms**: State changes (hover, menu open)
+- **300-500ms**: Layout changes (accordion, modal)
+- **500-800ms**: Entrance animations (page load)
+
+**Easing curves (use these, not CSS defaults):**
+```css
+/* Recommended - natural deceleration */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);    /* Smooth, refined */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);   /* Slightly snappier */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);     /* Confident, decisive */
+
+/* AVOID - feel dated and tacky */
+/* bounce: cubic-bezier(0.34, 1.56, 0.64, 1); */
+/* elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6); */
+```
+
+**Exit animations are faster than entrances.** Use ~75% of enter duration.
+
+### CSS Animations
+```css
+/* Prefer for simple, declarative animations */
+- transitions for state changes
+- @keyframes for complex sequences
+- transform + opacity only (GPU-accelerated)
+```
+
+### JavaScript Animation
+```javascript
+/* Use for complex, interactive animations */
+- Web Animations API for programmatic control
+- Framer Motion for React
+- GSAP for complex sequences
+```
+
+### Performance
+- **GPU acceleration**: Use `transform` and `opacity`, avoid layout properties
+- **will-change**: Add sparingly for known expensive animations
+- **Reduce paint**: Minimize repaints, use `contain` where appropriate
+- **Monitor FPS**: Ensure 60fps on target devices
+
+### Accessibility
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**NEVER**:
+- Use bounce or elastic easing curves—they feel dated and draw attention to the animation itself
+- Animate layout properties (width, height, top, left)—use transform instead
+- Use durations over 500ms for feedback—it feels laggy
+- Animate without purpose—every animation needs a reason
+- Ignore `prefers-reduced-motion`—this is an accessibility violation
+- Animate everything—animation fatigue makes interfaces feel exhausting
+- Block interaction during animations unless intentional
+
+## Verify Quality
+
+Test animations thoroughly:
+
+- **Smooth at 60fps**: No jank on target devices
+- **Feels natural**: Easing curves feel organic, not robotic
+- **Appropriate timing**: Not too fast (jarring) or too slow (laggy)
+- **Reduced motion works**: Animations disabled or simplified appropriately
+- **Doesn't block**: Users can interact during/after animations
+- **Adds value**: Makes interface clearer or more delightful
+
+Remember: Motion should enhance understanding and provide feedback, not just add decoration. Animate with purpose, respect performance constraints, and always consider accessibility. Great animation is invisible - it just makes everything feel right.

source/skills/audit/SKILL.md 🔗

@@ -0,0 +1,147 @@
+---
+name: audit
+description: "Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review."
+argument-hint: "[area (feature, page, component...)]"
+user-invocable: true
+---
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first.
+
+---
+
+Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues — document them for other commands to address.
+
+This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
+
+## Diagnostic Scan
+
+Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below.
+
+### 1. Accessibility (A11y)
+
+**Check for**:
+- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA)
+- **Missing ARIA**: Interactive elements without proper roles, labels, or states
+- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps
+- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons
+- **Alt text**: Missing or poor image descriptions
+- **Form issues**: Inputs without labels, poor error messaging, missing required indicators
+
+**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA)
+
+### 2. Performance
+
+**Check for**:
+- **Layout thrashing**: Reading/writing layout properties in loops
+- **Expensive animations**: Animating layout properties (width, height, top, left) instead of transform/opacity
+- **Missing optimization**: Images without lazy loading, unoptimized assets, missing will-change
+- **Bundle size**: Unnecessary imports, unused dependencies
+- **Render performance**: Unnecessary re-renders, missing memoization
+
+**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized)
+
+### 3. Theming
+
+**Check for**:
+- **Hard-coded colors**: Colors not using design tokens
+- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme
+- **Inconsistent tokens**: Using wrong tokens, mixing token types
+- **Theme switching issues**: Values that don't update on theme change
+
+**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly)
+
+### 4. Responsive Design
+
+**Check for**:
+- **Fixed widths**: Hard-coded widths that break on mobile
+- **Touch targets**: Interactive elements < 44x44px
+- **Horizontal scroll**: Content overflow on narrow viewports
+- **Text scaling**: Layouts that break when text size increases
+- **Missing breakpoints**: No mobile/tablet variants
+
+**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets)
+
+### 5. Anti-Patterns (CRITICAL)
+
+Check against ALL the **DON'T** guidelines in the impeccable skill. Look for AI slop tells (AI color palette, gradient text, glassmorphism, hero metrics, card grids, generic fonts) and general design anti-patterns (gray on color, nested cards, bounce easing, redundant copy).
+
+**Score 0-4**: 0=AI slop gallery (5+ tells), 1=Heavy AI aesthetic (3-4 tells), 2=Some tells (1-2 noticeable), 3=Mostly clean (subtle issues only), 4=No AI tells (distinctive, intentional design)
+
+## Generate Report
+
+### Audit Health Score
+
+| # | Dimension | Score | Key Finding |
+|---|-----------|-------|-------------|
+| 1 | Accessibility | ? | [most critical a11y issue or "--"] |
+| 2 | Performance | ? | |
+| 3 | Responsive Design | ? | |
+| 4 | Theming | ? | |
+| 5 | Anti-Patterns | ? | |
+| **Total** | | **??/20** | **[Rating band]** |
+
+**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues)
+
+### Anti-Patterns Verdict
+**Start here.** Pass/fail: Does this look AI-generated? List specific tells. Be brutally honest.
+
+### Executive Summary
+- Audit Health Score: **??/20** ([rating band])
+- Total issues found (count by severity: P0/P1/P2/P3)
+- Top 3-5 critical issues
+- Recommended next steps
+
+### Detailed Findings by Severity
+
+Tag every issue with **P0-P3 severity**:
+- **P0 Blocking**: Prevents task completion — fix immediately
+- **P1 Major**: Significant difficulty or WCAG AA violation — fix before release
+- **P2 Minor**: Annoyance, workaround exists — fix in next pass
+- **P3 Polish**: Nice-to-fix, no real user impact — fix if time permits
+
+For each issue, document:
+- **[P?] Issue name**
+- **Location**: Component, file, line
+- **Category**: Accessibility / Performance / Theming / Responsive / Anti-Pattern
+- **Impact**: How it affects users
+- **WCAG/Standard**: Which standard it violates (if applicable)
+- **Recommendation**: How to fix it
+- **Suggested command**: Which command to use (prefer: {{available_commands}})
+
+### Patterns & Systemic Issues
+
+Identify recurring problems that indicate systemic gaps rather than one-off mistakes:
+- "Hard-coded colors appear in 15+ components, should use design tokens"
+- "Touch targets consistently too small (<44px) throughout mobile experience"
+
+### Positive Findings
+
+Note what's working well — good practices to maintain and replicate.
+
+## Recommended Actions
+
+List recommended commands in priority order (P0 first, then P1, then P2):
+
+1. **[P?] `{{command_prefix}}command-name`** — Brief description (specific context from audit findings)
+2. **[P?] `{{command_prefix}}command-name`** — Brief description (specific context)
+
+**Rules**: Only recommend commands from: {{available_commands}}. Map findings to the most appropriate command. End with `{{command_prefix}}polish` as the final step if any fixes were recommended.
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `{{command_prefix}}audit` after fixes to see your score improve.
+
+**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters.
+
+**NEVER**:
+- Report issues without explaining impact (why does this matter?)
+- Provide generic recommendations (be specific and actionable)
+- Skip positive findings (celebrate what works)
+- Forget to prioritize (everything can't be P0)
+- Report false positives without verification
+
+Remember: You're a technical quality auditor. Document systematically, prioritize ruthlessly, cite specific code locations, and provide clear paths to improvement.

source/skills/bolder/SKILL.md 🔗

@@ -0,0 +1,116 @@
+---
+name: bolder
+description: "Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Increase visual impact and personality in designs that are too safe, generic, or visually underwhelming, creating more engaging and memorable experiences.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too safe or boring:
+
+1. **Identify weakness sources**:
+   - **Generic choices**: System fonts, basic colors, standard layouts
+   - **Timid scale**: Everything is medium-sized with no drama
+   - **Low contrast**: Everything has similar visual weight
+   - **Static**: No motion, no energy, no life
+   - **Predictable**: Standard patterns with no surprises
+   - **Flat hierarchy**: Nothing stands out or commands attention
+
+2. **Understand the context**:
+   - What's the brand personality? (How far can we push?)
+   - What's the purpose? (Marketing can be bolder than financial dashboards)
+   - Who's the audience? (What will resonate?)
+   - What are the constraints? (Brand guidelines, accessibility, performance)
+
+If any of these are unclear from the codebase, {{ask_instruction}}
+
+**CRITICAL**: "Bolder" doesn't mean chaotic or garish. It means distinctive, memorable, and confident. Think intentional drama, not random chaos.
+
+**WARNING - AI SLOP TRAP**: When making things "bolder," AI defaults to the same tired tricks: cyan/purple gradients, glassmorphism, neon accents on dark backgrounds, gradient text on metrics. These are the OPPOSITE of bold—they're generic. Review ALL the DON'T guidelines in the impeccable skill before proceeding. Bold means distinctive, not "more effects."
+
+## Plan Amplification
+
+Create a strategy to increase impact while maintaining coherence:
+
+- **Focal point**: What should be the hero moment? (Pick ONE, make it amazing)
+- **Personality direction**: Maximalist chaos? Elegant drama? Playful energy? Dark moody? Choose a lane.
+- **Risk budget**: How experimental can we be? Push boundaries within constraints.
+- **Hierarchy amplification**: Make big things BIGGER, small things smaller (increase contrast)
+
+**IMPORTANT**: Bold design must still be usable. Impact without function is just decoration.
+
+## Amplify the Design
+
+Systematically increase impact across these dimensions:
+
+### Typography Amplification
+- **Replace generic fonts**: Swap system fonts for distinctive choices (see impeccable skill for inspiration)
+- **Extreme scale**: Create dramatic size jumps (3x-5x differences, not 1.5x)
+- **Weight contrast**: Pair 900 weights with 200 weights, not 600 with 400
+- **Unexpected choices**: Variable fonts, display fonts for headlines, condensed/extended widths, monospace as intentional accent (not as lazy "dev tool" default)
+
+### Color Intensification
+- **Increase saturation**: Shift to more vibrant, energetic colors (but not neon)
+- **Bold palette**: Introduce unexpected color combinations—avoid the purple-blue gradient AI slop
+- **Dominant color strategy**: Let one bold color own 60% of the design
+- **Sharp accents**: High-contrast accent colors that pop
+- **Tinted neutrals**: Replace pure grays with tinted grays that harmonize with your palette
+- **Rich gradients**: Intentional multi-stop gradients (not generic purple-to-blue)
+
+### Spatial Drama
+- **Extreme scale jumps**: Make important elements 3-5x larger than surroundings
+- **Break the grid**: Let hero elements escape containers and cross boundaries
+- **Asymmetric layouts**: Replace centered, balanced layouts with tension-filled asymmetry
+- **Generous space**: Use white space dramatically (100-200px gaps, not 20-40px)
+- **Overlap**: Layer elements intentionally for depth
+
+### Visual Effects
+- **Dramatic shadows**: Large, soft shadows for elevation (but not generic drop shadows on rounded rectangles)
+- **Background treatments**: Mesh patterns, noise textures, geometric patterns, intentional gradients (not purple-to-blue)
+- **Texture & depth**: Grain, halftone, duotone, layered elements—NOT glassmorphism (it's overused AI slop)
+- **Borders & frames**: Thick borders, decorative frames, custom shapes (not rounded rectangles with colored border on one side)
+- **Custom elements**: Illustrative elements, custom icons, decorative details that reinforce brand
+
+### Motion & Animation
+- **Entrance choreography**: Staggered, dramatic page load animations with 50-100ms delays
+- **Scroll effects**: Parallax, reveal animations, scroll-triggered sequences
+- **Micro-interactions**: Satisfying hover effects, click feedback, state changes
+- **Transitions**: Smooth, noticeable transitions using ease-out-quart/quint/expo (not bounce or elastic—they cheapen the effect)
+
+### Composition Boldness
+- **Hero moments**: Create clear focal points with dramatic treatment
+- **Diagonal flows**: Escape horizontal/vertical rigidity with diagonal arrangements
+- **Full-bleed elements**: Use full viewport width/height for impact
+- **Unexpected proportions**: Golden ratio? Throw it out. Try 70/30, 80/20 splits
+
+**NEVER**:
+- Add effects randomly without purpose (chaos ≠ bold)
+- Sacrifice readability for aesthetics (body text must be readable)
+- Make everything bold (then nothing is bold - need contrast)
+- Ignore accessibility (bold design must still meet WCAG standards)
+- Overwhelm with motion (animation fatigue is real)
+- Copy trendy aesthetics blindly (bold means distinctive, not derivative)
+
+## Verify Quality
+
+Ensure amplification maintains usability and coherence:
+
+- **NOT AI slop**: Does this look like every other AI-generated "bold" design? If yes, start over.
+- **Still functional**: Can users accomplish tasks without distraction?
+- **Coherent**: Does everything feel intentional and unified?
+- **Memorable**: Will users remember this experience?
+- **Performant**: Do all these effects run smoothly?
+- **Accessible**: Does it still meet accessibility standards?
+
+**The test**: If you showed this to someone and said "AI made this bolder," would they believe you immediately? If yes, you've failed. Bold means distinctive, not "more AI effects."
+
+Remember: Bold design is confident design. It takes risks, makes statements, and creates memorable experiences. But bold without strategy is just loud. Be intentional, be dramatic, be unforgettable.

source/skills/clarify/SKILL.md 🔗

@@ -0,0 +1,183 @@
+---
+name: clarify
+description: "Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Identify and improve unclear, confusing, or poorly written interface text to make the product easier to understand and use.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first. Additionally gather: audience technical level and users' mental state in context.
+
+---
+
+## Assess Current Copy
+
+Identify what makes the text unclear or ineffective:
+
+1. **Find clarity problems**:
+   - **Jargon**: Technical terms users won't understand
+   - **Ambiguity**: Multiple interpretations possible
+   - **Passive voice**: "Your file has been uploaded" vs "We uploaded your file"
+   - **Length**: Too wordy or too terse
+   - **Assumptions**: Assuming user knowledge they don't have
+   - **Missing context**: Users don't know what to do or why
+   - **Tone mismatch**: Too formal, too casual, or inappropriate for situation
+
+2. **Understand the context**:
+   - Who's the audience? (Technical? General? First-time users?)
+   - What's the user's mental state? (Stressed during error? Confident during success?)
+   - What's the action? (What do we want users to do?)
+   - What's the constraint? (Character limits? Space limitations?)
+
+**CRITICAL**: Clear copy helps users succeed. Unclear copy creates frustration, errors, and support tickets.
+
+## Plan Copy Improvements
+
+Create a strategy for clearer communication:
+
+- **Primary message**: What's the ONE thing users need to know?
+- **Action needed**: What should users do next (if anything)?
+- **Tone**: How should this feel? (Helpful? Apologetic? Encouraging?)
+- **Constraints**: Length limits, brand voice, localization considerations
+
+**IMPORTANT**: Good UX writing is invisible. Users should understand immediately without noticing the words.
+
+## Improve Copy Systematically
+
+Refine text across these common areas:
+
+### Error Messages
+**Bad**: "Error 403: Forbidden"
+**Good**: "You don't have permission to view this page. Contact your admin for access."
+
+**Bad**: "Invalid input"
+**Good**: "Email addresses need an @ symbol. Try: name@example.com"
+
+**Principles**:
+- Explain what went wrong in plain language
+- Suggest how to fix it
+- Don't blame the user
+- Include examples when helpful
+- Link to help/support if applicable
+
+### Form Labels & Instructions
+**Bad**: "DOB (MM/DD/YYYY)"
+**Good**: "Date of birth" (with placeholder showing format)
+
+**Bad**: "Enter value here"
+**Good**: "Your email address" or "Company name"
+
+**Principles**:
+- Use clear, specific labels (not generic placeholders)
+- Show format expectations with examples
+- Explain why you're asking (when not obvious)
+- Put instructions before the field, not after
+- Keep required field indicators clear
+
+### Button & CTA Text
+**Bad**: "Click here" | "Submit" | "OK"
+**Good**: "Create account" | "Save changes" | "Got it, thanks"
+
+**Principles**:
+- Describe the action specifically
+- Use active voice (verb + noun)
+- Match user's mental model
+- Be specific ("Save" is better than "OK")
+
+### Help Text & Tooltips
+**Bad**: "This is the username field"
+**Good**: "Choose a username. You can change this later in Settings."
+
+**Principles**:
+- Add value (don't just repeat the label)
+- Answer the implicit question ("What is this?" or "Why do you need this?")
+- Keep it brief but complete
+- Link to detailed docs if needed
+
+### Empty States
+**Bad**: "No items"
+**Good**: "No projects yet. Create your first project to get started."
+
+**Principles**:
+- Explain why it's empty (if not obvious)
+- Show next action clearly
+- Make it welcoming, not dead-end
+
+### Success Messages
+**Bad**: "Success"
+**Good**: "Settings saved! Your changes will take effect immediately."
+
+**Principles**:
+- Confirm what happened
+- Explain what happens next (if relevant)
+- Be brief but complete
+- Match the user's emotional moment (celebrate big wins)
+
+### Loading States
+**Bad**: "Loading..." (for 30+ seconds)
+**Good**: "Analyzing your data... this usually takes 30-60 seconds"
+
+**Principles**:
+- Set expectations (how long?)
+- Explain what's happening (when it's not obvious)
+- Show progress when possible
+- Offer escape hatch if appropriate ("Cancel")
+
+### Confirmation Dialogs
+**Bad**: "Are you sure?"
+**Good**: "Delete 'Project Alpha'? This can't be undone."
+
+**Principles**:
+- State the specific action
+- Explain consequences (especially for destructive actions)
+- Use clear button labels ("Delete project" not "Yes")
+- Don't overuse confirmations (only for risky actions)
+
+### Navigation & Wayfinding
+**Bad**: Generic labels like "Items" | "Things" | "Stuff"
+**Good**: Specific labels like "Your projects" | "Team members" | "Settings"
+
+**Principles**:
+- Be specific and descriptive
+- Use language users understand (not internal jargon)
+- Make hierarchy clear
+- Consider information scent (breadcrumbs, current location)
+
+## Apply Clarity Principles
+
+Every piece of copy should follow these rules:
+
+1. **Be specific**: "Enter email" not "Enter value"
+2. **Be concise**: Cut unnecessary words (but don't sacrifice clarity)
+3. **Be active**: "Save changes" not "Changes will be saved"
+4. **Be human**: "Oops, something went wrong" not "System error encountered"
+5. **Be helpful**: Tell users what to do, not just what happened
+6. **Be consistent**: Use same terms throughout (don't vary for variety)
+
+**NEVER**:
+- Use jargon without explanation
+- Blame users ("You made an error" → "This field is required")
+- Be vague ("Something went wrong" without explanation)
+- Use passive voice unnecessarily
+- Write overly long explanations (be concise)
+- Use humor for errors (be empathetic instead)
+- Assume technical knowledge
+- Vary terminology (pick one term and stick with it)
+- Repeat information (headers restating intros, redundant explanations)
+- Use placeholders as the only labels (they disappear when users type)
+
+## Verify Improvements
+
+Test that copy improvements work:
+
+- **Comprehension**: Can users understand without context?
+- **Actionability**: Do users know what to do next?
+- **Brevity**: Is it as short as possible while remaining clear?
+- **Consistency**: Does it match terminology elsewhere?
+- **Tone**: Is it appropriate for the situation?
+
+Remember: You're a clarity expert with excellent communication skills. Write like you're explaining to a smart friend who's unfamiliar with the product. Be clear, be helpful, be human.
+

source/skills/colorize/SKILL.md 🔗

@@ -0,0 +1,142 @@
+---
+name: colorize
+description: "Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Strategically introduce color to designs that are too monochromatic, gray, or lacking in visual warmth and personality.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first. Additionally gather: existing brand colors.
+
+---
+
+## Assess Color Opportunity
+
+Analyze the current state and identify opportunities:
+
+1. **Understand current state**:
+   - **Color absence**: Pure grayscale? Limited neutrals? One timid accent?
+   - **Missed opportunities**: Where could color add meaning, hierarchy, or delight?
+   - **Context**: What's appropriate for this domain and audience?
+   - **Brand**: Are there existing brand colors we should use?
+
+2. **Identify where color adds value**:
+   - **Semantic meaning**: Success (green), error (red), warning (yellow/orange), info (blue)
+   - **Hierarchy**: Drawing attention to important elements
+   - **Categorization**: Different sections, types, or states
+   - **Emotional tone**: Warmth, energy, trust, creativity
+   - **Wayfinding**: Helping users navigate and understand structure
+   - **Delight**: Moments of visual interest and personality
+
+If any of these are unclear from the codebase, {{ask_instruction}}
+
+**CRITICAL**: More color ≠ better. Strategic color beats rainbow vomit every time. Every color should have a purpose.
+
+## Plan Color Strategy
+
+Create a purposeful color introduction plan:
+
+- **Color palette**: What colors match the brand/context? (Choose 2-4 colors max beyond neutrals)
+- **Dominant color**: Which color owns 60% of colored elements?
+- **Accent colors**: Which colors provide contrast and highlights? (30% and 10%)
+- **Application strategy**: Where does each color appear and why?
+
+**IMPORTANT**: Color should enhance hierarchy and meaning, not create chaos. Less is more when it matters more.
+
+## Introduce Color Strategically
+
+Add color systematically across these dimensions:
+
+### Semantic Color
+- **State indicators**:
+  - Success: Green tones (emerald, forest, mint)
+  - Error: Red/pink tones (rose, crimson, coral)
+  - Warning: Orange/amber tones
+  - Info: Blue tones (sky, ocean, indigo)
+  - Neutral: Gray/slate for inactive states
+
+- **Status badges**: Colored backgrounds or borders for states (active, pending, completed, etc.)
+- **Progress indicators**: Colored bars, rings, or charts showing completion or health
+
+### Accent Color Application
+- **Primary actions**: Color the most important buttons/CTAs
+- **Links**: Add color to clickable text (maintain accessibility)
+- **Icons**: Colorize key icons for recognition and personality
+- **Headers/titles**: Add color to section headers or key labels
+- **Hover states**: Introduce color on interaction
+
+### Background & Surfaces
+- **Tinted backgrounds**: Replace pure gray (`#f5f5f5`) with warm neutrals (`oklch(97% 0.01 60)`) or cool tints (`oklch(97% 0.01 250)`)
+- **Colored sections**: Use subtle background colors to separate areas
+- **Gradient backgrounds**: Add depth with subtle, intentional gradients (not generic purple-blue)
+- **Cards & surfaces**: Tint cards or surfaces slightly for warmth
+
+**Use OKLCH for color**: It's perceptually uniform, meaning equal steps in lightness *look* equal. Great for generating harmonious scales.
+
+### Data Visualization
+- **Charts & graphs**: Use color to encode categories or values
+- **Heatmaps**: Color intensity shows density or importance
+- **Comparison**: Color coding for different datasets or timeframes
+
+### Borders & Accents
+- **Accent borders**: Add colored left/top borders to cards or sections
+- **Underlines**: Color underlines for emphasis or active states
+- **Dividers**: Subtle colored dividers instead of gray lines
+- **Focus rings**: Colored focus indicators matching brand
+
+### Typography Color
+- **Colored headings**: Use brand colors for section headings (maintain contrast)
+- **Highlight text**: Color for emphasis or categories
+- **Labels & tags**: Small colored labels for metadata or categories
+
+### Decorative Elements
+- **Illustrations**: Add colored illustrations or icons
+- **Shapes**: Geometric shapes in brand colors as background elements
+- **Gradients**: Colorful gradient overlays or mesh backgrounds
+- **Blobs/organic shapes**: Soft colored shapes for visual interest
+
+## Balance & Refinement
+
+Ensure color addition improves rather than overwhelms:
+
+### Maintain Hierarchy
+- **Dominant color** (60%): Primary brand color or most used accent
+- **Secondary color** (30%): Supporting color for variety
+- **Accent color** (10%): High contrast for key moments
+- **Neutrals** (remaining): Gray/black/white for structure
+
+### Accessibility
+- **Contrast ratios**: Ensure WCAG compliance (4.5:1 for text, 3:1 for UI components)
+- **Don't rely on color alone**: Use icons, labels, or patterns alongside color
+- **Test for color blindness**: Verify red/green combinations work for all users
+
+### Cohesion
+- **Consistent palette**: Use colors from defined palette, not arbitrary choices
+- **Systematic application**: Same color meanings throughout (green always = success)
+- **Temperature consistency**: Warm palette stays warm, cool stays cool
+
+**NEVER**:
+- Use every color in the rainbow (choose 2-4 colors beyond neutrals)
+- Apply color randomly without semantic meaning
+- Put gray text on colored backgrounds—it looks washed out; use a darker shade of the background color or transparency instead
+- Use pure gray for neutrals—add subtle color tint (warm or cool) for sophistication
+- Use pure black (`#000`) or pure white (`#fff`) for large areas
+- Violate WCAG contrast requirements
+- Use color as the only indicator (accessibility issue)
+- Make everything colorful (defeats the purpose)
+- Default to purple-blue gradients (AI slop aesthetic)
+
+## Verify Color Addition
+
+Test that colorization improves the experience:
+
+- **Better hierarchy**: Does color guide attention appropriately?
+- **Clearer meaning**: Does color help users understand states/categories?
+- **More engaging**: Does the interface feel warmer and more inviting?
+- **Still accessible**: Do all color combinations meet WCAG standards?
+- **Not overwhelming**: Is color balanced and purposeful?
+
+Remember: Color is emotional and powerful. Use it to create warmth, guide attention, communicate meaning, and express personality. But restraint and strategy matter more than saturation and variety. Be colorful, but be intentional.

source/skills/critique/SKILL.md 🔗

@@ -0,0 +1,226 @@
+---
+name: critique
+description: "Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component."
+argument-hint: "[area (feature, page, component...)]"
+user-invocable: true
+allowed-tools:
+  - Bash(npx impeccable *)
+---
+
+## STEPS
+
+### Step 1: Preparation
+
+Invoke {{command_prefix}}impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run {{command_prefix}}impeccable teach first. Additionally gather: what the interface is trying to accomplish.
+
+### Step 2: Gather Assessments
+
+Launch two independent assessments. **Neither must see the other's output** to avoid bias.
+
+You SHOULD delegate each assessment to a separate sub-agent for independence. Use your environment's agent spawning mechanism (e.g., Claude Code's `Agent` tool, or Codex's subagent spawning). Sub-agents should return their findings as structured text. Do NOT output findings to the user yet.
+
+If sub-agents are not available in the current environment, complete each assessment sequentially, writing findings to internal notes before proceeding.
+
+**Tab isolation**: When browser automation is available, each assessment MUST create its own new tab. Never reuse an existing tab, even if one is already open at the correct URL. This prevents the two assessments from interfering with each other's page state.
+
+#### Assessment A: LLM Design Review
+
+Read the relevant source files (HTML, CSS, JS/TS) and, if browser automation is available, visually inspect the live page. **Create a new tab** for this; do not reuse existing tabs. After navigation, label the tab by setting the document title:
+```javascript
+document.title = '[LLM] ' + document.title;
+```
+Think like a design director. Evaluate:
+
+**AI Slop Detection (CRITICAL)**: Does this look like every other AI-generated interface? Review against ALL **DON'T** guidelines in the impeccable skill. Check for AI color palette, gradient text, dark glows, glassmorphism, hero metric layouts, identical card grids, generic fonts, and all other tells. **The test**: If someone said "AI made this," would you believe them immediately?
+
+**Holistic Design Review**: visual hierarchy (eye flow, primary action clarity), information architecture (structure, grouping, cognitive load), emotional resonance (does it match brand and audience?), discoverability (are interactive elements obvious?), composition (balance, whitespace, rhythm), typography (hierarchy, readability, font choices), color (purposeful use, cohesion, accessibility), states & edge cases (empty, loading, error, success), microcopy (clarity, tone, helpfulness).
+
+**Cognitive Load** (consult [cognitive-load](reference/cognitive-load.md)):
+- Run the 8-item cognitive load checklist. Report failure count: 0-1 = low (good), 2-3 = moderate, 4+ = critical.
+- Count visible options at each decision point. If >4, flag it.
+- Check for progressive disclosure: is complexity revealed only when needed?
+
+**Emotional Journey**:
+- What emotion does this interface evoke? Is that intentional?
+- **Peak-end rule**: Is the most intense moment positive? Does the experience end well?
+- **Emotional valleys**: Check for anxiety spikes at high-stakes moments (payment, delete, commit). Are there design interventions (progress indicators, reassurance copy, undo options)?
+
+**Nielsen's Heuristics** (consult [heuristics-scoring](reference/heuristics-scoring.md)):
+Score each of the 10 heuristics 0-4. This scoring will be presented in the report.
+
+Return structured findings covering: AI slop verdict, heuristic scores, cognitive load assessment, what's working (2-3 items), priority issues (3-5 with what/why/fix), minor observations, and provocative questions.
+
+#### Assessment B: Automated Detection
+
+Run the bundled deterministic detector, which flags 25 specific patterns (AI slop tells + general design quality).
+
+**CLI scan**:
+```bash
+npx impeccable --json [--fast] [target]
+```
+
+- Pass HTML/JSX/TSX/Vue/Svelte files or directories as `[target]` (anything with markup). Do not pass CSS-only files.
+- For URLs, skip the CLI scan (it requires Puppeteer). Use browser visualization instead.
+- For large directories (200+ scannable files), use `--fast` (regex-only, skips jsdom)
+- For 500+ files, narrow scope or ask the user
+- Exit code 0 = clean, 2 = findings
+
+**Browser visualization** (when browser automation tools are available AND the target is a viewable page):
+
+The overlay is a **visual aid for the user**. It highlights issues directly in their browser. Do NOT scroll through the page to screenshot overlays. Instead, read the console output to get the results programmatically.
+
+1. **Start the live detection server**:
+   ```bash
+   npx impeccable live &
+   ```
+   Note the port printed to stdout (auto-assigned). Use `--port=PORT` to fix it.
+2. **Create a new tab** and navigate to the page (use dev server URL for local files, or direct URL). Do not reuse existing tabs.
+3. **Label the tab** via `javascript_tool` so the user can distinguish it:
+   ```javascript
+   document.title = '[Human] ' + document.title;
+   ```
+4. **Scroll to top** to ensure the page is scrolled to the very top before injection
+5. **Inject** via `javascript_tool` (replace PORT with the port from step 1):
+   ```javascript
+   const s = document.createElement('script'); s.src = 'http://localhost:PORT/detect.js'; document.head.appendChild(s);
+   ```
+6. Wait 2-3 seconds for the detector to render overlays
+7. **Read results from console** using `read_console_messages` with pattern `impeccable`. The detector logs all findings with the `[impeccable]` prefix. Do NOT scroll through the page to take screenshots of the overlays.
+8. **Cleanup**: Stop the live server when done:
+   ```bash
+   npx impeccable live stop
+   ```
+
+For multi-view targets, inject on 3-5 representative pages. If injection fails, continue with CLI results only.
+
+Return: CLI findings (JSON), browser console findings (if applicable), and any false positives noted.
+
+### Step 3: Generate Combined Critique Report
+
+Synthesize both assessments into a single report. Do NOT simply concatenate. Weave the findings together, noting where the LLM review and detector agree, where the detector caught issues the LLM missed, and where detector findings are false positives.
+
+Structure your feedback as a design director would:
+
+#### Design Health Score
+> *Consult [heuristics-scoring](reference/heuristics-scoring.md)*
+
+Present the Nielsen's 10 heuristics scores as a table:
+
+| # | Heuristic | Score | Key Issue |
+|---|-----------|-------|-----------|
+| 1 | Visibility of System Status | ? | [specific finding or "n/a" if solid] |
+| 2 | Match System / Real World | ? | |
+| 3 | User Control and Freedom | ? | |
+| 4 | Consistency and Standards | ? | |
+| 5 | Error Prevention | ? | |
+| 6 | Recognition Rather Than Recall | ? | |
+| 7 | Flexibility and Efficiency | ? | |
+| 8 | Aesthetic and Minimalist Design | ? | |
+| 9 | Error Recovery | ? | |
+| 10 | Help and Documentation | ? | |
+| **Total** | | **??/40** | **[Rating band]** |
+
+Be honest with scores. A 4 means genuinely excellent. Most real interfaces score 20-32.
+
+#### Anti-Patterns Verdict
+
+**Start here.** Does this look AI-generated?
+
+**LLM assessment**: Your own evaluation of AI slop tells. Cover overall aesthetic feel, layout sameness, generic composition, missed opportunities for personality.
+
+**Deterministic scan**: Summarize what the automated detector found, with counts and file locations. Note any additional issues the detector caught that you missed, and flag any false positives.
+
+**Visual overlays** (if browser was used): Tell the user that overlays are now visible in the **[Human]** tab in their browser, highlighting the detected issues. Summarize what the console output reported.
+
+#### Overall Impression
+A brief gut reaction: what works, what doesn't, and the single biggest opportunity.
+
+#### What's Working
+Highlight 2-3 things done well. Be specific about why they work.
+
+#### Priority Issues
+The 3-5 most impactful design problems, ordered by importance.
+
+For each issue, tag with **P0-P3 severity** (consult [heuristics-scoring](reference/heuristics-scoring.md) for severity definitions):
+- **[P?] What**: Name the problem clearly
+- **Why it matters**: How this hurts users or undermines goals
+- **Fix**: What to do about it (be concrete)
+- **Suggested command**: Which command could address this (from: {{available_commands}})
+
+#### Persona Red Flags
+> *Consult [personas](reference/personas.md)*
+
+Auto-select 2-3 personas most relevant to this interface type (use the selection table in the reference). If `{{config_file}}` contains a `## Design Context` section from `impeccable teach`, also generate 1-2 project-specific personas from the audience/brand info.
+
+For each selected persona, walk through the primary user action and list specific red flags found:
+
+**Alex (Power User)**: No keyboard shortcuts detected. Form requires 8 clicks for primary action. Forced modal onboarding. High abandonment risk.
+
+**Jordan (First-Timer)**: Icon-only nav in sidebar. Technical jargon in error messages ("404 Not Found"). No visible help. Will abandon at step 2.
+
+Be specific. Name the exact elements and interactions that fail each persona. Don't write generic persona descriptions; write what broke for them.
+
+#### Minor Observations
+Quick notes on smaller issues worth addressing.
+
+#### Questions to Consider
+Provocative questions that might unlock better solutions:
+- "What if the primary action were more prominent?"
+- "Does this need to feel this complex?"
+- "What would a confident version of this look like?"
+
+**Remember**:
+- Be direct. Vague feedback wastes everyone's time.
+- Be specific. "The submit button," not "some elements."
+- Say what's wrong AND why it matters to users.
+- Give concrete suggestions, not just "consider exploring..."
+- Prioritize ruthlessly. If everything is important, nothing is.
+- Don't soften criticism. Developers need honest feedback to ship great design.
+
+### Step 4: Ask the User
+
+**After presenting findings**, use targeted questions based on what was actually found. {{ask_instruction}} These answers will shape the action plan.
+
+Ask questions along these lines (adapt to the specific findings; do NOT ask generic questions):
+
+1. **Priority direction**: Based on the issues found, ask which category matters most to the user right now. For example: "I found problems with visual hierarchy, color usage, and information overload. Which area should we tackle first?" Offer the top 2-3 issue categories as options.
+
+2. **Design intent**: If the critique found a tonal mismatch, ask whether it was intentional. For example: "The interface feels clinical and corporate. Is that the intended tone, or should it feel warmer/bolder/more playful?" Offer 2-3 tonal directions as options based on what would fix the issues found.
+
+3. **Scope**: Ask how much the user wants to take on. For example: "I found N issues. Want to address everything, or focus on the top 3?" Offer scope options like "Top 3 only", "All issues", "Critical issues only".
+
+4. **Constraints** (optional; only ask if relevant): If the findings touch many areas, ask if anything is off-limits. For example: "Should any sections stay as-is?" This prevents the plan from touching things the user considers done.
+
+**Rules for questions**:
+- Every question must reference specific findings from the report. Never ask generic "who is your audience?" questions.
+- Keep it to 2-4 questions maximum. Respect the user's time.
+- Offer concrete options, not open-ended prompts.
+- If findings are straightforward (e.g., only 1-2 clear issues), skip questions and go directly to Step 5.
+
+### Step 5: Recommended Actions
+
+**After receiving the user's answers**, present a prioritized action summary reflecting the user's priorities and scope from Step 4.
+
+#### Action Summary
+
+List recommended commands in priority order, based on the user's answers:
+
+1. **`{{command_prefix}}command-name`**: Brief description of what to fix (specific context from critique findings)
+2. **`{{command_prefix}}command-name`**: Brief description (specific context)
+...
+
+**Rules for recommendations**:
+- Only recommend commands from: {{available_commands}}
+- Order by the user's stated priorities first, then by impact
+- Each item's description should carry enough context that the command knows what to focus on
+- Map each Priority Issue to the appropriate command
+- Skip commands that would address zero issues
+- If the user chose a limited scope, only include items within that scope
+- If the user marked areas as off-limits, exclude commands that would touch those areas
+- End with `{{command_prefix}}polish` as the final step if any fixes were recommended
+
+After presenting the summary, tell the user:
+
+> You can ask me to run these one at a time, all at once, or in any order you prefer.
+>
+> Re-run `{{command_prefix}}critique` after fixes to see your score improve.

source/skills/critique/reference/cognitive-load.md 🔗

@@ -0,0 +1,106 @@
+# Cognitive Load Assessment
+
+Cognitive load is the total mental effort required to use an interface. Overloaded users make mistakes, get frustrated, and leave. This reference helps identify and fix cognitive overload.
+
+---
+
+## Three Types of Cognitive Load
+
+### Intrinsic Load — The Task Itself
+Complexity inherent to what the user is trying to do. You can't eliminate this, but you can structure it.
+
+**Manage it by**:
+- Breaking complex tasks into discrete steps
+- Providing scaffolding (templates, defaults, examples)
+- Progressive disclosure — show what's needed now, hide the rest
+- Grouping related decisions together
+
+### Extraneous Load — Bad Design
+Mental effort caused by poor design choices. **Eliminate this ruthlessly** — it's pure waste.
+
+**Common sources**:
+- Confusing navigation that requires mental mapping
+- Unclear labels that force users to guess meaning
+- Visual clutter competing for attention
+- Inconsistent patterns that prevent learning
+- Unnecessary steps between user intent and result
+
+### Germane Load — Learning Effort
+Mental effort spent building understanding. This is *good* cognitive load — it leads to mastery.
+
+**Support it by**:
+- Progressive disclosure that reveals complexity gradually
+- Consistent patterns that reward learning
+- Feedback that confirms correct understanding
+- Onboarding that teaches through action, not walls of text
+
+---
+
+## Cognitive Load Checklist
+
+Evaluate the interface against these 8 items:
+
+- [ ] **Single focus**: Can the user complete their primary task without distraction from competing elements?
+- [ ] **Chunking**: Is information presented in digestible groups (≤4 items per group)?
+- [ ] **Grouping**: Are related items visually grouped together (proximity, borders, shared background)?
+- [ ] **Visual hierarchy**: Is it immediately clear what's most important on the screen?
+- [ ] **One thing at a time**: Can the user focus on a single decision before moving to the next?
+- [ ] **Minimal choices**: Are decisions simplified (≤4 visible options at any decision point)?
+- [ ] **Working memory**: Does the user need to remember information from a previous screen to act on the current one?
+- [ ] **Progressive disclosure**: Is complexity revealed only when the user needs it?
+
+**Scoring**: Count the failed items. 0–1 failures = low cognitive load (good). 2–3 = moderate (address soon). 4+ = high cognitive load (critical fix needed).
+
+---
+
+## The Working Memory Rule
+
+**Humans can hold ≤4 items in working memory at once** (Miller's Law revised by Cowan, 2001).
+
+At any decision point, count the number of distinct options, actions, or pieces of information a user must simultaneously consider:
+- **≤4 items**: Within working memory limits — manageable
+- **5–7 items**: Pushing the boundary — consider grouping or progressive disclosure
+- **8+ items**: Overloaded — users will skip, misclick, or abandon
+
+**Practical applications**:
+- Navigation menus: ≤5 top-level items (group the rest under clear categories)
+- Form sections: ≤4 fields visible per group before a visual break
+- Action buttons: 1 primary, 1–2 secondary, group the rest in a menu
+- Dashboard widgets: ≤4 key metrics visible without scrolling
+- Pricing tiers: ≤3 options (more causes analysis paralysis)
+
+---
+
+## Common Cognitive Load Violations
+
+### 1. The Wall of Options
+**Problem**: Presenting 10+ choices at once with no hierarchy.
+**Fix**: Group into categories, highlight recommended, use progressive disclosure.
+
+### 2. The Memory Bridge
+**Problem**: User must remember info from step 1 to complete step 3.
+**Fix**: Keep relevant context visible, or repeat it where it's needed.
+
+### 3. The Hidden Navigation
+**Problem**: User must build a mental map of where things are.
+**Fix**: Always show current location (breadcrumbs, active states, progress indicators).
+
+### 4. The Jargon Barrier
+**Problem**: Technical or domain language forces translation effort.
+**Fix**: Use plain language. If domain terms are unavoidable, define them inline.
+
+### 5. The Visual Noise Floor
+**Problem**: Every element has the same visual weight — nothing stands out.
+**Fix**: Establish clear hierarchy: one primary element, 2–3 secondary, everything else muted.
+
+### 6. The Inconsistent Pattern
+**Problem**: Similar actions work differently in different places.
+**Fix**: Standardize interaction patterns. Same type of action = same type of UI.
+
+### 7. The Multi-Task Demand
+**Problem**: Interface requires processing multiple simultaneous inputs (reading + deciding + navigating).
+**Fix**: Sequence the steps. Let the user do one thing at a time.
+
+### 8. The Context Switch
+**Problem**: User must jump between screens/tabs/modals to gather info for a single decision.
+**Fix**: Co-locate the information needed for each decision. Reduce back-and-forth.

source/skills/critique/reference/heuristics-scoring.md 🔗

@@ -0,0 +1,234 @@
+# Heuristics Scoring Guide
+
+Score each of Nielsen's 10 Usability Heuristics on a 0–4 scale. Be honest — a 4 means genuinely excellent, not "good enough."
+
+## Nielsen's 10 Heuristics
+
+### 1. Visibility of System Status
+
+Keep users informed about what's happening through timely, appropriate feedback.
+
+**Check for**:
+- Loading indicators during async operations
+- Confirmation of user actions (save, submit, delete)
+- Progress indicators for multi-step processes
+- Current location in navigation (breadcrumbs, active states)
+- Form validation feedback (inline, not just on submit)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No feedback — user is guessing what happened |
+| 1 | Rare feedback — most actions produce no visible response |
+| 2 | Partial — some states communicated, major gaps remain |
+| 3 | Good — most operations give clear feedback, minor gaps |
+| 4 | Excellent — every action confirms, progress is always visible |
+
+### 2. Match Between System and Real World
+
+Speak the user's language. Follow real-world conventions. Information appears in natural, logical order.
+
+**Check for**:
+- Familiar terminology (no unexplained jargon)
+- Logical information order matching user expectations
+- Recognizable icons and metaphors
+- Domain-appropriate language for the target audience
+- Natural reading flow (left-to-right, top-to-bottom priority)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Pure tech jargon, alien to users |
+| 1 | Mostly confusing — requires domain expertise to navigate |
+| 2 | Mixed — some plain language, some jargon leaks through |
+| 3 | Mostly natural — occasional term needs context |
+| 4 | Speaks the user's language fluently throughout |
+
+### 3. User Control and Freedom
+
+Users need a clear "emergency exit" from unwanted states without extended dialogue.
+
+**Check for**:
+- Undo/redo functionality
+- Cancel buttons on forms and modals
+- Clear navigation back to safety (home, previous)
+- Easy way to clear filters, search, selections
+- Escape from long or multi-step processes
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Users get trapped — no way out without refreshing |
+| 1 | Difficult exits — must find obscure paths to escape |
+| 2 | Some exits — main flows have escape, edge cases don't |
+| 3 | Good control — users can exit and undo most actions |
+| 4 | Full control — undo, cancel, back, and escape everywhere |
+
+### 4. Consistency and Standards
+
+Users shouldn't wonder whether different words, situations, or actions mean the same thing.
+
+**Check for**:
+- Consistent terminology throughout the interface
+- Same actions produce same results everywhere
+- Platform conventions followed (standard UI patterns)
+- Visual consistency (colors, typography, spacing, components)
+- Consistent interaction patterns (same gesture = same behavior)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Inconsistent everywhere — feels like different products stitched together |
+| 1 | Many inconsistencies — similar things look/behave differently |
+| 2 | Partially consistent — main flows match, details diverge |
+| 3 | Mostly consistent — occasional deviation, nothing confusing |
+| 4 | Fully consistent — cohesive system, predictable behavior |
+
+### 5. Error Prevention
+
+Better than good error messages is a design that prevents problems in the first place.
+
+**Check for**:
+- Confirmation before destructive actions (delete, overwrite)
+- Constraints preventing invalid input (date pickers, dropdowns)
+- Smart defaults that reduce errors
+- Clear labels that prevent misunderstanding
+- Autosave and draft recovery
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Errors easy to make — no guardrails anywhere |
+| 1 | Few safeguards — some inputs validated, most aren't |
+| 2 | Partial prevention — common errors caught, edge cases slip |
+| 3 | Good prevention — most error paths blocked proactively |
+| 4 | Excellent — errors nearly impossible through smart constraints |
+
+### 6. Recognition Rather Than Recall
+
+Minimize memory load. Make objects, actions, and options visible or easily retrievable.
+
+**Check for**:
+- Visible options (not buried in hidden menus)
+- Contextual help when needed (tooltips, inline hints)
+- Recent items and history
+- Autocomplete and suggestions
+- Labels on icons (not icon-only navigation)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Heavy memorization — users must remember paths and commands |
+| 1 | Mostly recall — many hidden features, few visible cues |
+| 2 | Some aids — main actions visible, secondary features hidden |
+| 3 | Good recognition — most things discoverable, few memory demands |
+| 4 | Everything discoverable — users never need to memorize |
+
+### 7. Flexibility and Efficiency of Use
+
+Accelerators — invisible to novices — speed up expert interaction.
+
+**Check for**:
+- Keyboard shortcuts for common actions
+- Customizable interface elements
+- Recent items and favorites
+- Bulk/batch actions
+- Power user features that don't complicate the basics
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | One rigid path — no shortcuts or alternatives |
+| 1 | Limited flexibility — few alternatives to the main path |
+| 2 | Some shortcuts — basic keyboard support, limited bulk actions |
+| 3 | Good accelerators — keyboard nav, some customization |
+| 4 | Highly flexible — multiple paths, power features, customizable |
+
+### 8. Aesthetic and Minimalist Design
+
+Interfaces should not contain irrelevant or rarely needed information. Every element should serve a purpose.
+
+**Check for**:
+- Only necessary information visible at each step
+- Clear visual hierarchy directing attention
+- Purposeful use of color and emphasis
+- No decorative clutter competing for attention
+- Focused, uncluttered layouts
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Overwhelming — everything competes for attention equally |
+| 1 | Cluttered — too much noise, hard to find what matters |
+| 2 | Some clutter — main content clear, periphery noisy |
+| 3 | Mostly clean — focused design, minor visual noise |
+| 4 | Perfectly minimal — every element earns its pixel |
+
+### 9. Help Users Recognize, Diagnose, and Recover from Errors
+
+Error messages should use plain language, precisely indicate the problem, and constructively suggest a solution.
+
+**Check for**:
+- Plain language error messages (no error codes for users)
+- Specific problem identification ("Email is missing @" not "Invalid input")
+- Actionable recovery suggestions
+- Errors displayed near the source of the problem
+- Non-blocking error handling (don't wipe the form)
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | Cryptic errors — codes, jargon, or no message at all |
+| 1 | Vague errors — "Something went wrong" with no guidance |
+| 2 | Clear but unhelpful — names the problem but not the fix |
+| 3 | Clear with suggestions — identifies problem and offers next steps |
+| 4 | Perfect recovery — pinpoints issue, suggests fix, preserves user work |
+
+### 10. Help and Documentation
+
+Even if the system is usable without docs, help should be easy to find, task-focused, and concise.
+
+**Check for**:
+- Searchable help or documentation
+- Contextual help (tooltips, inline hints, guided tours)
+- Task-focused organization (not feature-organized)
+- Concise, scannable content
+- Easy access without leaving current context
+
+**Scoring**:
+| Score | Criteria |
+|-------|----------|
+| 0 | No help available anywhere |
+| 1 | Help exists but hard to find or irrelevant |
+| 2 | Basic help — FAQ or docs exist, not contextual |
+| 3 | Good documentation — searchable, mostly task-focused |
+| 4 | Excellent contextual help — right info at the right moment |
+
+---
+
+## Score Summary
+
+**Total possible**: 40 points (10 heuristics × 4 max)
+
+| Score Range | Rating | What It Means |
+|-------------|--------|---------------|
+| 36–40 | Excellent | Minor polish only — ship it |
+| 28–35 | Good | Address weak areas, solid foundation |
+| 20–27 | Acceptable | Significant improvements needed before users are happy |
+| 12–19 | Poor | Major UX overhaul required — core experience broken |
+| 0–11 | Critical | Redesign needed — unusable in current state |
+
+---
+
+## Issue Severity (P0–P3)
+
+Tag each individual issue found during scoring with a priority level:
+
+| Priority | Name | Description | Action |
+|----------|------|-------------|--------|
+| **P0** | Blocking | Prevents task completion entirely | Fix immediately — this is a showstopper |
+| **P1** | Major | Causes significant difficulty or confusion | Fix before release |
+| **P2** | Minor | Annoyance, but workaround exists | Fix in next pass |
+| **P3** | Polish | Nice-to-fix, no real user impact | Fix if time permits |
+
+**Tip**: If you're unsure between two levels, ask: "Would a user contact support about this?" If yes, it's at least P1.

source/skills/critique/reference/personas.md 🔗

@@ -0,0 +1,178 @@
+# Persona-Based Design Testing
+
+Test the interface through the eyes of 5 distinct user archetypes. Each persona exposes different failure modes that a single "design director" perspective would miss.
+
+**How to use**: Select 2–3 personas most relevant to the interface being critiqued. Walk through the primary user action as each persona. Report specific red flags — not generic concerns.
+
+---
+
+## 1. Impatient Power User — "Alex"
+
+**Profile**: Expert with similar products. Expects efficiency, hates hand-holding. Will find shortcuts or leave.
+
+**Behaviors**:
+- Skips all onboarding and instructions
+- Looks for keyboard shortcuts immediately
+- Tries to bulk-select, batch-edit, and automate
+- Gets frustrated by required steps that feel unnecessary
+- Abandons if anything feels slow or patronizing
+
+**Test Questions**:
+- Can Alex complete the core task in under 60 seconds?
+- Are there keyboard shortcuts for common actions?
+- Can onboarding be skipped entirely?
+- Do modals have keyboard dismiss (Esc)?
+- Is there a "power user" path (shortcuts, bulk actions)?
+
+**Red Flags** (report these specifically):
+- Forced tutorials or unskippable onboarding
+- No keyboard navigation for primary actions
+- Slow animations that can't be skipped
+- One-item-at-a-time workflows where batch would be natural
+- Redundant confirmation steps for low-risk actions
+
+---
+
+## 2. Confused First-Timer — "Jordan"
+
+**Profile**: Never used this type of product. Needs guidance at every step. Will abandon rather than figure it out.
+
+**Behaviors**:
+- Reads all instructions carefully
+- Hesitates before clicking anything unfamiliar
+- Looks for help or support constantly
+- Misunderstands jargon and abbreviations
+- Takes the most literal interpretation of any label
+
+**Test Questions**:
+- Is the first action obviously clear within 5 seconds?
+- Are all icons labeled with text?
+- Is there contextual help at decision points?
+- Does terminology assume prior knowledge?
+- Is there a clear "back" or "undo" at every step?
+
+**Red Flags** (report these specifically):
+- Icon-only navigation with no labels
+- Technical jargon without explanation
+- No visible help option or guidance
+- Ambiguous next steps after completing an action
+- No confirmation that an action succeeded
+
+---
+
+## 3. Accessibility-Dependent User — "Sam"
+
+**Profile**: Uses screen reader (VoiceOver/NVDA), keyboard-only navigation. May have low vision, motor impairment, or cognitive differences.
+
+**Behaviors**:
+- Tabs through the interface linearly
+- Relies on ARIA labels and heading structure
+- Cannot see hover states or visual-only indicators
+- Needs adequate color contrast (4.5:1 minimum)
+- May use browser zoom up to 200%
+
+**Test Questions**:
+- Can the entire primary flow be completed keyboard-only?
+- Are all interactive elements focusable with visible focus indicators?
+- Do images have meaningful alt text?
+- Is color contrast WCAG AA compliant (4.5:1 for text)?
+- Does the screen reader announce state changes (loading, success, errors)?
+
+**Red Flags** (report these specifically):
+- Click-only interactions with no keyboard alternative
+- Missing or invisible focus indicators
+- Meaning conveyed by color alone (red = error, green = success)
+- Unlabeled form fields or buttons
+- Time-limited actions without extension option
+- Custom components that break screen reader flow
+
+---
+
+## 4. Deliberate Stress Tester — "Riley"
+
+**Profile**: Methodical user who pushes interfaces beyond the happy path. Tests edge cases, tries unexpected inputs, and probes for gaps in the experience.
+
+**Behaviors**:
+- Tests edge cases intentionally (empty states, long strings, special characters)
+- Submits forms with unexpected data (emoji, RTL text, very long values)
+- Tries to break workflows by navigating backwards, refreshing mid-flow, or opening in multiple tabs
+- Looks for inconsistencies between what the UI promises and what actually happens
+- Documents problems methodically
+
+**Test Questions**:
+- What happens at the edges (0 items, 1000 items, very long text)?
+- Do error states recover gracefully or leave the UI in a broken state?
+- What happens on refresh mid-workflow? Is state preserved?
+- Are there features that appear to work but produce broken results?
+- How does the UI handle unexpected input (emoji, special chars, paste from Excel)?
+
+**Red Flags** (report these specifically):
+- Features that appear to work but silently fail or produce wrong results
+- Error handling that exposes technical details or leaves UI in a broken state
+- Empty states that show nothing useful ("No results" with no guidance)
+- Workflows that lose user data on refresh or navigation
+- Inconsistent behavior between similar interactions in different parts of the UI
+
+---
+
+## 5. Distracted Mobile User — "Casey"
+
+**Profile**: Using phone one-handed on the go. Frequently interrupted. Possibly on a slow connection.
+
+**Behaviors**:
+- Uses thumb only — prefers bottom-of-screen actions
+- Gets interrupted mid-flow and returns later
+- Switches between apps frequently
+- Has limited attention span and low patience
+- Types as little as possible, prefers taps and selections
+
+**Test Questions**:
+- Are primary actions in the thumb zone (bottom half of screen)?
+- Is state preserved if the user leaves and returns?
+- Does it work on slow connections (3G)?
+- Can forms leverage autocomplete and smart defaults?
+- Are touch targets at least 44×44pt?
+
+**Red Flags** (report these specifically):
+- Important actions positioned at the top of the screen (unreachable by thumb)
+- No state persistence — progress lost on tab switch or interruption
+- Large text inputs required where selection would work
+- Heavy assets loading on every page (no lazy loading)
+- Tiny tap targets or targets too close together
+
+---
+
+## Selecting Personas
+
+Choose personas based on the interface type:
+
+| Interface Type | Primary Personas | Why |
+|---------------|-----------------|-----|
+| Landing page / marketing | Jordan, Riley, Casey | First impressions, trust, mobile |
+| Dashboard / admin | Alex, Sam | Power users, accessibility |
+| E-commerce / checkout | Casey, Riley, Jordan | Mobile, edge cases, clarity |
+| Onboarding flow | Jordan, Casey | Confusion, interruption |
+| Data-heavy / analytics | Alex, Sam | Efficiency, keyboard nav |
+| Form-heavy / wizard | Jordan, Sam, Casey | Clarity, accessibility, mobile |
+
+---
+
+## Project-Specific Personas
+
+If `{{config_file}}` contains a `## Design Context` section (generated by `impeccable teach`), derive 1–2 additional personas from the audience and brand information:
+
+1. Read the target audience description
+2. Identify the primary user archetype not covered by the 5 predefined personas
+3. Create a persona following this template:
+
+```
+### [Role] — "[Name]"
+
+**Profile**: [2-3 key characteristics derived from Design Context]
+
+**Behaviors**: [3-4 specific behaviors based on the described audience]
+
+**Red Flags**: [3-4 things that would alienate this specific user type]
+```
+
+Only generate project-specific personas when real Design Context data is available. Don't invent audience details — use the 5 predefined personas when no context exists.

source/skills/delight/SKILL.md 🔗

@@ -0,0 +1,303 @@
+---
+name: delight
+description: "Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Identify opportunities to add moments of joy, personality, and unexpected polish that transform functional interfaces into delightful experiences.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first. Additionally gather: what's appropriate for the domain (playful vs professional vs quirky vs elegant).
+
+---
+
+## Assess Delight Opportunities
+
+Identify where delight would enhance (not distract from) the experience:
+
+1. **Find natural delight moments**:
+   - **Success states**: Completed actions (save, send, publish)
+   - **Empty states**: First-time experiences, onboarding
+   - **Loading states**: Waiting periods that could be entertaining
+   - **Achievements**: Milestones, streaks, completions
+   - **Interactions**: Hover states, clicks, drags
+   - **Errors**: Softening frustrating moments
+   - **Easter eggs**: Hidden discoveries for curious users
+
+2. **Understand the context**:
+   - What's the brand personality? (Playful? Professional? Quirky? Elegant?)
+   - Who's the audience? (Tech-savvy? Creative? Corporate?)
+   - What's the emotional context? (Accomplishment? Exploration? Frustration?)
+   - What's appropriate? (Banking app ≠ gaming app)
+
+3. **Define delight strategy**:
+   - **Subtle sophistication**: Refined micro-interactions (luxury brands)
+   - **Playful personality**: Whimsical illustrations and copy (consumer apps)
+   - **Helpful surprises**: Anticipating needs before users ask (productivity tools)
+   - **Sensory richness**: Satisfying sounds, smooth animations (creative tools)
+
+If any of these are unclear from the codebase, {{ask_instruction}}
+
+**CRITICAL**: Delight should enhance usability, never obscure it. If users notice the delight more than accomplishing their goal, you've gone too far.
+
+## Delight Principles
+
+Follow these guidelines:
+
+### Delight Amplifies, Never Blocks
+- Delight moments should be quick (< 1 second)
+- Never delay core functionality for delight
+- Make delight skippable or subtle
+- Respect user's time and task focus
+
+### Surprise and Discovery
+- Hide delightful details for users to discover
+- Reward exploration and curiosity
+- Don't announce every delight moment
+- Let users share discoveries with others
+
+### Appropriate to Context
+- Match delight to emotional moment (celebrate success, empathize with errors)
+- Respect the user's state (don't be playful during critical errors)
+- Match brand personality and audience expectations
+- Cultural sensitivity (what's delightful varies by culture)
+
+### Compound Over Time
+- Delight should remain fresh with repeated use
+- Vary responses (not same animation every time)
+- Reveal deeper layers with continued use
+- Build anticipation through patterns
+
+## Delight Techniques
+
+Add personality and joy through these methods:
+
+### Micro-interactions & Animation
+
+**Button delight**:
+```css
+/* Satisfying button press */
+.button {
+  transition: transform 0.1s, box-shadow 0.1s;
+}
+.button:active {
+  transform: translateY(2px);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+/* Ripple effect on click */
+/* Smooth lift on hover */
+.button:hover {
+  transform: translateY(-2px);
+  transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1); /* ease-out-quart */
+}
+```
+
+**Loading delight**:
+- Playful loading animations (not just spinners)
+- Personality in loading messages (write product-specific ones, not generic AI filler)
+- Progress indication with encouraging messages
+- Skeleton screens with subtle animations
+
+**Success animations**:
+- Checkmark draw animation
+- Confetti burst for major achievements
+- Gentle scale + fade for confirmation
+- Satisfying sound effects (subtle)
+
+**Hover surprises**:
+- Icons that animate on hover
+- Color shifts or glow effects
+- Tooltip reveals with personality
+- Cursor changes (custom cursors for branded experiences)
+
+### Personality in Copy
+
+**Playful error messages**:
+```
+"Error 404"
+"This page is playing hide and seek. (And winning)"
+
+"Connection failed"
+"Looks like the internet took a coffee break. Want to retry?"
+```
+
+**Encouraging empty states**:
+```
+"No projects"
+"Your canvas awaits. Create something amazing."
+
+"No messages"
+"Inbox zero! You're crushing it today."
+```
+
+**Playful labels & tooltips**:
+```
+"Delete"
+"Send to void" (for playful brand)
+
+"Help"
+"Rescue me" (tooltip)
+```
+
+**IMPORTANT**: Match copy personality to brand. Banks shouldn't be wacky, but they can be warm.
+
+### Illustrations & Visual Personality
+
+**Custom illustrations**:
+- Empty state illustrations (not stock icons)
+- Error state illustrations (friendly monsters, quirky characters)
+- Loading state illustrations (animated characters)
+- Success state illustrations (celebrations)
+
+**Icon personality**:
+- Custom icon set matching brand personality
+- Animated icons (subtle motion on hover/click)
+- Illustrative icons (more detailed than generic)
+- Consistent style across all icons
+
+**Background effects**:
+- Subtle particle effects
+- Gradient mesh backgrounds
+- Geometric patterns
+- Parallax depth
+- Time-of-day themes (morning vs night)
+
+### Satisfying Interactions
+
+**Drag and drop delight**:
+- Lift effect on drag (shadow, scale)
+- Snap animation when dropped
+- Satisfying placement sound
+- Undo toast ("Dropped in wrong place? [Undo]")
+
+**Toggle switches**:
+- Smooth slide with spring physics
+- Color transition
+- Haptic feedback on mobile
+- Optional sound effect
+
+**Progress & achievements**:
+- Streak counters with celebratory milestones
+- Progress bars that "celebrate" at 100%
+- Badge unlocks with animation
+- Playful stats ("You're on fire! 5 days in a row")
+
+**Form interactions**:
+- Input fields that animate on focus
+- Checkboxes with a satisfying scale pulse when checked
+- Success state that celebrates valid input
+- Auto-grow textareas
+
+### Sound Design
+
+**Subtle audio cues** (when appropriate):
+- Notification sounds (distinctive but not annoying)
+- Success sounds (satisfying "ding")
+- Error sounds (empathetic, not harsh)
+- Typing sounds for chat/messaging
+- Ambient background audio (very subtle)
+
+**IMPORTANT**:
+- Respect system sound settings
+- Provide mute option
+- Keep volumes quiet (subtle cues, not alarms)
+- Don't play on every interaction (sound fatigue is real)
+
+### Easter Eggs & Hidden Delights
+
+**Discovery rewards**:
+- Konami code unlocks special theme
+- Hidden keyboard shortcuts (Cmd+K for special features)
+- Hover reveals on logos or illustrations
+- Alt text jokes on images (for screen reader users too!)
+- Console messages for developers ("Like what you see? We're hiring!")
+
+**Seasonal touches**:
+- Holiday themes (subtle, tasteful)
+- Seasonal color shifts
+- Weather-based variations
+- Time-based changes (dark at night, light during day)
+
+**Contextual personality**:
+- Different messages based on time of day
+- Responses to specific user actions
+- Randomized variations (not same every time)
+- Progressive reveals with continued use
+
+### Loading & Waiting States
+
+**Make waiting engaging**:
+- Interesting loading messages that rotate
+- Progress bars with personality
+- Mini-games during long loads
+- Fun facts or tips while waiting
+- Countdown with encouraging messages
+
+```
+Loading messages — write ones specific to your product, not generic AI filler:
+- "Crunching your latest numbers..."
+- "Syncing with your team's changes..."
+- "Preparing your dashboard..."
+- "Checking for updates since yesterday..."
+```
+
+**WARNING**: Avoid cliched loading messages like "Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball", "Counting backwards from infinity". These are AI-slop copy — instantly recognizable as machine-generated. Write messages that are specific to what your product actually does.
+
+### Celebration Moments
+
+**Success celebrations**:
+- Confetti for major milestones
+- Animated checkmarks for completions
+- Progress bar celebrations at 100%
+- "Achievement unlocked" style notifications
+- Personalized messages ("You published your 10th article!")
+
+**Milestone recognition**:
+- First-time actions get special treatment
+- Streak tracking and celebration
+- Progress toward goals
+- Anniversary celebrations
+
+## Implementation Patterns
+
+**Animation libraries**:
+- Framer Motion (React)
+- GSAP (universal)
+- Lottie (After Effects animations)
+- Canvas confetti (party effects)
+
+**Sound libraries**:
+- Howler.js (audio management)
+- Use-sound (React hook)
+
+**Physics libraries**:
+- React Spring (spring physics)
+- Popmotion (animation primitives)
+
+**IMPORTANT**: File size matters. Compress images, optimize animations, lazy load delight features.
+
+**NEVER**:
+- Delay core functionality for delight
+- Force users through delightful moments (make skippable)
+- Use delight to hide poor UX
+- Overdo it (less is more)
+- Ignore accessibility (animate responsibly, provide alternatives)
+- Make every interaction delightful (special moments should be special)
+- Sacrifice performance for delight
+- Be inappropriate for context (read the room)
+
+## Verify Delight Quality
+
+Test that delight actually delights:
+
+- **User reactions**: Do users smile? Share screenshots?
+- **Doesn't annoy**: Still pleasant after 100th time?
+- **Doesn't block**: Can users opt out or skip?
+- **Performant**: No jank, no slowdown
+- **Appropriate**: Matches brand and context
+- **Accessible**: Works with reduced motion, screen readers
+
+Remember: Delight is the difference between a tool and an experience. Add personality, surprise users positively, and create moments worth sharing. But always respect usability - delight should enhance, never obstruct.

source/skills/distill/SKILL.md 🔗

@@ -0,0 +1,121 @@
+---
+name: distill
+description: "Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Remove unnecessary complexity from designs, revealing the essential elements and creating clarity through ruthless simplification.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel complex or cluttered:
+
+1. **Identify complexity sources**:
+   - **Too many elements**: Competing buttons, redundant information, visual clutter
+   - **Excessive variation**: Too many colors, fonts, sizes, styles without purpose
+   - **Information overload**: Everything visible at once, no progressive disclosure
+   - **Visual noise**: Unnecessary borders, shadows, backgrounds, decorations
+   - **Confusing hierarchy**: Unclear what matters most
+   - **Feature creep**: Too many options, actions, or paths forward
+
+2. **Find the essence**:
+   - What's the primary user goal? (There should be ONE)
+   - What's actually necessary vs nice-to-have?
+   - What can be removed, hidden, or combined?
+   - What's the 20% that delivers 80% of value?
+
+If any of these are unclear from the codebase, {{ask_instruction}}
+
+**CRITICAL**: Simplicity is not about removing features - it's about removing obstacles between users and their goals. Every element should justify its existence.
+
+## Plan Simplification
+
+Create a ruthless editing strategy:
+
+- **Core purpose**: What's the ONE thing this should accomplish?
+- **Essential elements**: What's truly necessary to achieve that purpose?
+- **Progressive disclosure**: What can be hidden until needed?
+- **Consolidation opportunities**: What can be combined or integrated?
+
+**IMPORTANT**: Simplification is hard. It requires saying no to good ideas to make room for great execution. Be ruthless.
+
+## Simplify the Design
+
+Systematically remove complexity across these dimensions:
+
+### Information Architecture
+- **Reduce scope**: Remove secondary actions, optional features, redundant information
+- **Progressive disclosure**: Hide complexity behind clear entry points (accordions, modals, step-through flows)
+- **Combine related actions**: Merge similar buttons, consolidate forms, group related content
+- **Clear hierarchy**: ONE primary action, few secondary actions, everything else tertiary or hidden
+- **Remove redundancy**: If it's said elsewhere, don't repeat it here
+
+### Visual Simplification
+- **Reduce color palette**: Use 1-2 colors plus neutrals, not 5-7 colors
+- **Limit typography**: One font family, 3-4 sizes maximum, 2-3 weights
+- **Remove decorations**: Eliminate borders, shadows, backgrounds that don't serve hierarchy or function
+- **Flatten structure**: Reduce nesting, remove unnecessary containers—never nest cards inside cards
+- **Remove unnecessary cards**: Cards aren't needed for basic layout; use spacing and alignment instead
+- **Consistent spacing**: Use one spacing scale, remove arbitrary gaps
+
+### Layout Simplification
+- **Linear flow**: Replace complex grids with simple vertical flow where possible
+- **Remove sidebars**: Move secondary content inline or hide it
+- **Full-width**: Use available space generously instead of complex multi-column layouts
+- **Consistent alignment**: Pick left or center, stick with it
+- **Generous white space**: Let content breathe, don't pack everything tight
+
+### Interaction Simplification
+- **Reduce choices**: Fewer buttons, fewer options, clearer path forward (paradox of choice is real)
+- **Smart defaults**: Make common choices automatic, only ask when necessary
+- **Inline actions**: Replace modal flows with inline editing where possible
+- **Remove steps**: Can signup be one step instead of three? Can checkout be simplified?
+- **Clear CTAs**: ONE obvious next step, not five competing actions
+
+### Content Simplification
+- **Shorter copy**: Cut every sentence in half, then do it again
+- **Active voice**: "Save changes" not "Changes will be saved"
+- **Remove jargon**: Plain language always wins
+- **Scannable structure**: Short paragraphs, bullet points, clear headings
+- **Essential information only**: Remove marketing fluff, legalese, hedging
+- **Remove redundant copy**: No headers restating intros, no repeated explanations, say it once
+
+### Code Simplification
+- **Remove unused code**: Dead CSS, unused components, orphaned files
+- **Flatten component trees**: Reduce nesting depth
+- **Consolidate styles**: Merge similar styles, use utilities consistently
+- **Reduce variants**: Does that component need 12 variations, or can 3 cover 90% of cases?
+
+**NEVER**:
+- Remove necessary functionality (simplicity ≠ feature-less)
+- Sacrifice accessibility for simplicity (clear labels and ARIA still required)
+- Make things so simple they're unclear (mystery ≠ minimalism)
+- Remove information users need to make decisions
+- Eliminate hierarchy completely (some things should stand out)
+- Oversimplify complex domains (match complexity to actual task complexity)
+
+## Verify Simplification
+
+Ensure simplification improves usability:
+
+- **Faster task completion**: Can users accomplish goals more quickly?
+- **Reduced cognitive load**: Is it easier to understand what to do?
+- **Still complete**: Are all necessary features still accessible?
+- **Clearer hierarchy**: Is it obvious what matters most?
+- **Better performance**: Does simpler design load faster?
+
+## Document Removed Complexity
+
+If you removed features or options:
+- Document why they were removed
+- Consider if they need alternative access points
+- Note any user feedback to monitor
+
+Remember: You have great taste and judgment. Simplification is an act of confidence - knowing what to keep and courage to remove the rest. As Antoine de Saint-Exupéry said: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away."

source/skills/harden/SKILL.md 🔗

@@ -0,0 +1,389 @@
+---
+name: harden
+description: "Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
+
+## Assess Hardening Needs
+
+Identify weaknesses and edge cases:
+
+1. **Test with extreme inputs**:
+   - Very long text (names, descriptions, titles)
+   - Very short text (empty, single character)
+   - Special characters (emoji, RTL text, accents)
+   - Large numbers (millions, billions)
+   - Many items (1000+ list items, 50+ options)
+   - No data (empty states)
+
+2. **Test error scenarios**:
+   - Network failures (offline, slow, timeout)
+   - API errors (400, 401, 403, 404, 500)
+   - Validation errors
+   - Permission errors
+   - Rate limiting
+   - Concurrent operations
+
+3. **Test internationalization**:
+   - Long translations (German is often 30% longer than English)
+   - RTL languages (Arabic, Hebrew)
+   - Character sets (Chinese, Japanese, Korean, emoji)
+   - Date/time formats
+   - Number formats (1,000 vs 1.000)
+   - Currency symbols
+
+**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
+
+## Hardening Dimensions
+
+Systematically improve resilience:
+
+### Text Overflow & Wrapping
+
+**Long text handling**:
+```css
+/* Single line with ellipsis */
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Multi-line with clamp */
+.line-clamp {
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Allow wrapping */
+.wrap {
+  word-wrap: break-word;
+  overflow-wrap: break-word;
+  hyphens: auto;
+}
+```
+
+**Flex/Grid overflow**:
+```css
+/* Prevent flex items from overflowing */
+.flex-item {
+  min-width: 0; /* Allow shrinking below content size */
+  overflow: hidden;
+}
+
+/* Prevent grid items from overflowing */
+.grid-item {
+  min-width: 0;
+  min-height: 0;
+}
+```
+
+**Responsive text sizing**:
+- Use `clamp()` for fluid typography
+- Set minimum readable sizes (14px on mobile)
+- Test text scaling (zoom to 200%)
+- Ensure containers expand with text
+
+### Internationalization (i18n)
+
+**Text expansion**:
+- Add 30-40% space budget for translations
+- Use flexbox/grid that adapts to content
+- Test with longest language (usually German)
+- Avoid fixed widths on text containers
+
+```jsx
+// ❌ Bad: Assumes short English text
+<button className="w-24">Submit</button>
+
+// ✅ Good: Adapts to content
+<button className="px-4 py-2">Submit</button>
+```
+
+**RTL (Right-to-Left) support**:
+```css
+/* Use logical properties */
+margin-inline-start: 1rem; /* Not margin-left */
+padding-inline: 1rem; /* Not padding-left/right */
+border-inline-end: 1px solid; /* Not border-right */
+
+/* Or use dir attribute */
+[dir="rtl"] .arrow { transform: scaleX(-1); }
+```
+
+**Character set support**:
+- Use UTF-8 encoding everywhere
+- Test with Chinese/Japanese/Korean (CJK) characters
+- Test with emoji (they can be 2-4 bytes)
+- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
+
+**Date/Time formatting**:
+```javascript
+// ✅ Use Intl API for proper formatting
+new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
+new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
+
+new Intl.NumberFormat('en-US', { 
+  style: 'currency', 
+  currency: 'USD' 
+}).format(1234.56); // $1,234.56
+```
+
+**Pluralization**:
+```javascript
+// ❌ Bad: Assumes English pluralization
+`${count} item${count !== 1 ? 's' : ''}`
+
+// ✅ Good: Use proper i18n library
+t('items', { count }) // Handles complex plural rules
+```
+
+### Error Handling
+
+**Network errors**:
+- Show clear error messages
+- Provide retry button
+- Explain what happened
+- Offer offline mode (if applicable)
+- Handle timeout scenarios
+
+```jsx
+// Error states with recovery
+{error && (
+  <ErrorMessage>
+    <p>Failed to load data. {error.message}</p>
+    <button onClick={retry}>Try again</button>
+  </ErrorMessage>
+)}
+```
+
+**Form validation errors**:
+- Inline errors near fields
+- Clear, specific messages
+- Suggest corrections
+- Don't block submission unnecessarily
+- Preserve user input on error
+
+**API errors**:
+- Handle each status code appropriately
+  - 400: Show validation errors
+  - 401: Redirect to login
+  - 403: Show permission error
+  - 404: Show not found state
+  - 429: Show rate limit message
+  - 500: Show generic error, offer support
+
+**Graceful degradation**:
+- Core functionality works without JavaScript
+- Images have alt text
+- Progressive enhancement
+- Fallbacks for unsupported features
+
+### Edge Cases & Boundary Conditions
+
+**Empty states**:
+- No items in list
+- No search results
+- No notifications
+- No data to display
+- Provide clear next action
+
+**Loading states**:
+- Initial load
+- Pagination load
+- Refresh
+- Show what's loading ("Loading your projects...")
+- Time estimates for long operations
+
+**Large datasets**:
+- Pagination or virtual scrolling
+- Search/filter capabilities
+- Performance optimization
+- Don't load all 10,000 items at once
+
+**Concurrent operations**:
+- Prevent double-submission (disable button while loading)
+- Handle race conditions
+- Optimistic updates with rollback
+- Conflict resolution
+
+**Permission states**:
+- No permission to view
+- No permission to edit
+- Read-only mode
+- Clear explanation of why
+
+**Browser compatibility**:
+- Polyfills for modern features
+- Fallbacks for unsupported CSS
+- Feature detection (not browser detection)
+- Test in target browsers
+
+### Onboarding & First-Run Experience
+
+Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
+
+**Empty states**: Every zero-data screen needs:
+- What will appear here (description or illustration)
+- Why it matters to the user
+- Clear CTA to create the first item or start from a template
+- Visual interest (not just blank space with "No items yet")
+
+Empty state types to handle:
+- **First use**: emphasize value, provide templates
+- **User cleared**: light touch, easy to recreate
+- **No results**: suggest a different query, offer to clear filters
+- **No permissions**: explain why, how to get access
+
+**First-run experience**: Get users to their "aha moment" as quickly as possible.
+- Show, don't tell -- working examples over descriptions
+- Progressive disclosure -- teach one thing at a time, not everything upfront
+- Make onboarding optional -- let experienced users skip
+- Provide smart defaults so required setup is minimal
+
+**Feature discovery**: Teach features when users need them, not upfront.
+- Contextual tooltips at point of use (brief, dismissable, one-time)
+- Badges or indicators on new or unused features
+- Celebrate activation events quietly (a toast, not a modal)
+
+**NEVER**:
+- Force long onboarding before users can touch the product
+- Show the same tooltip repeatedly (track and respect dismissals)
+- Block the entire UI during a guided tour
+- Create separate tutorial modes disconnected from the real product
+- Design empty states that just say "No items" with no next action
+
+### Input Validation & Sanitization
+
+**Client-side validation**:
+- Required fields
+- Format validation (email, phone, URL)
+- Length limits
+- Pattern matching
+- Custom validation rules
+
+**Server-side validation** (always):
+- Never trust client-side only
+- Validate and sanitize all inputs
+- Protect against injection attacks
+- Rate limiting
+
+**Constraint handling**:
+```html
+<!-- Set clear constraints -->
+<input 
+  type="text"
+  maxlength="100"
+  pattern="[A-Za-z0-9]+"
+  required
+  aria-describedby="username-hint"
+/>
+<small id="username-hint">
+  Letters and numbers only, up to 100 characters
+</small>
+```
+
+### Accessibility Resilience
+
+**Keyboard navigation**:
+- All functionality accessible via keyboard
+- Logical tab order
+- Focus management in modals
+- Skip links for long content
+
+**Screen reader support**:
+- Proper ARIA labels
+- Announce dynamic changes (live regions)
+- Descriptive alt text
+- Semantic HTML
+
+**Motion sensitivity**:
+```css
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**High contrast mode**:
+- Test in Windows high contrast mode
+- Don't rely only on color
+- Provide alternative visual cues
+
+### Performance Resilience
+
+**Slow connections**:
+- Progressive image loading
+- Skeleton screens
+- Optimistic UI updates
+- Offline support (service workers)
+
+**Memory leaks**:
+- Clean up event listeners
+- Cancel subscriptions
+- Clear timers/intervals
+- Abort pending requests on unmount
+
+**Throttling & Debouncing**:
+```javascript
+// Debounce search input
+const debouncedSearch = debounce(handleSearch, 300);
+
+// Throttle scroll handler
+const throttledScroll = throttle(handleScroll, 100);
+```
+
+## Testing Strategies
+
+**Manual testing**:
+- Test with extreme data (very long, very short, empty)
+- Test in different languages
+- Test offline
+- Test slow connection (throttle to 3G)
+- Test with screen reader
+- Test keyboard-only navigation
+- Test on old browsers
+
+**Automated testing**:
+- Unit tests for edge cases
+- Integration tests for error scenarios
+- E2E tests for critical paths
+- Visual regression tests
+- Accessibility tests (axe, WAVE)
+
+**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
+
+**NEVER**:
+- Assume perfect input (validate everything)
+- Ignore internationalization (design for global)
+- Leave error messages generic ("Error occurred")
+- Forget offline scenarios
+- Trust client-side validation alone
+- Use fixed widths for text
+- Assume English-length text
+- Block entire interface when one component errors
+
+## Verify Hardening
+
+Test thoroughly with edge cases:
+
+- **Long text**: Try names with 100+ characters
+- **Emoji**: Use emoji in all text fields
+- **RTL**: Test with Arabic or Hebrew
+- **CJK**: Test with Chinese/Japanese/Korean
+- **Network issues**: Disable internet, throttle connection
+- **Large datasets**: Test with 1000+ items
+- **Concurrent actions**: Click submit 10 times rapidly
+- **Errors**: Force API errors, test all error states
+- **Empty**: Remove all data, test empty states
+
+Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.
+

source/skills/impeccable/SKILL.md 🔗

@@ -0,0 +1,364 @@
+---
+name: impeccable
+description: "Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' for shape-then-build, 'teach' for design context setup, or 'extract' to pull reusable components and tokens into the design system."
+argument-hint: "[craft|teach|extract]"
+user-invocable: true
+license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
+---
+
+<post-update-cleanup>
+BEFORE doing any design work, run this one-time maintenance step. Tell the user:
+
+> **Impeccable was updated.** A few skills were renamed or merged in this version (e.g. `/arrange` is now `/layout`, `/normalize` was folded into `/polish`). I'll clean up the old skill files so they don't clutter your project. This only runs once after an update.
+
+Then run:
+
+```bash
+node {{scripts_path}}/cleanup-deprecated.mjs
+```
+
+If the script removed files, briefly confirm what was cleaned up. If it found nothing, skip any output and move on.
+
+After running the script, delete this entire section (from `<post-update-cleanup>` through `</post-update-cleanup>` inclusive) from THIS file so it does not run again until the next update. Save the file.
+</post-update-cleanup>
+
+This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
+
+## Context Gathering Protocol
+
+Design skills produce generic output without project context. You MUST have confirmed design context before doing any design work.
+
+**Required context** (every design skill needs at minimum):
+- **Target audience**: Who uses this product and in what context?
+- **Use cases**: What jobs are they trying to get done?
+- **Brand personality/tone**: How should the interface feel?
+
+Individual skills may require additional context. Check the skill's preparation section for specifics.
+
+**CRITICAL**: You cannot infer this context by reading the codebase. Code tells you what was built, not who it's for or what it should feel like. Only the creator can provide this context.
+
+**Gathering order:**
+1. **Check current instructions (instant)**: If your loaded instructions already contain a **Design Context** section, proceed immediately.
+2. **Check .impeccable.md (fast)**: If not in instructions, read `.impeccable.md` from the project root. If it exists and contains the required context, proceed.
+3. **Run impeccable teach (REQUIRED)**: If neither source has context, you MUST run {{command_prefix}}impeccable teach NOW before doing anything else. Do NOT skip this step. Do NOT attempt to infer context from the codebase instead.
+
+---
+
+## Design Direction
+
+Commit to a BOLD aesthetic direction:
+- **Purpose**: What problem does this interface solve? Who uses it?
+- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
+- **Constraints**: Technical requirements (framework, performance, accessibility).
+- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
+
+**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work. The key is intentionality, not intensity.
+
+Then implement working code that is:
+- Production-grade and functional
+- Visually striking and memorable
+- Cohesive with a clear aesthetic point-of-view
+- Meticulously refined in every detail
+
+## Frontend Aesthetics Guidelines
+
+### Typography
+→ *Consult [typography reference](reference/typography.md) for OpenType features, web font loading, and the deeper material on scales.*
+
+Choose fonts that are beautiful, unique, and interesting. Pair a distinctive display font with a refined body font.
+
+<typography_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a modular type scale with fluid sizing (clamp) for headings on marketing/content pages. Use fixed `rem` scales for app UIs and dashboards (no major design system uses fluid type in product UI).
+- Use fewer sizes with more contrast. A 5-step scale with at least a 1.25 ratio between steps creates clearer hierarchy than 8 sizes that are 1.1× apart.
+- Line-height scales inversely with line length. Narrow columns want tighter leading, wide columns want more. For light text on dark backgrounds, ADD 0.05-0.1 to your normal line-height — light type reads as lighter weight and needs more breathing room.
+- Cap line length at ~65-75ch. Body text wider than that is fatiguing.
+</typography_principles>
+
+<font_selection_procedure>
+DO THIS BEFORE TYPING ANY FONT NAME.
+
+The model's natural failure mode is "I was told not to use Inter, so I will pick my next favorite font, which becomes the new monoculture." Avoid this by performing the following procedure on every project, in order:
+
+Step 1. Read the brief once. Write down 3 concrete words for the brand voice (e.g., "warm and mechanical and opinionated", "calm and clinical and careful", "fast and dense and unimpressed", "handmade and a little weird"). NOT "modern" or "elegant" — those are dead categories.
+
+Step 2. List the 3 fonts you would normally reach for given those words. Write them down. They are most likely from this list:
+
+<reflex_fonts_to_reject>
+Fraunces
+Newsreader
+Lora
+Crimson
+Crimson Pro
+Crimson Text
+Playfair Display
+Cormorant
+Cormorant Garamond
+Syne
+IBM Plex Mono
+IBM Plex Sans
+IBM Plex Serif
+Space Mono
+Space Grotesk
+Inter
+DM Sans
+DM Serif Display
+DM Serif Text
+Outfit
+Plus Jakarta Sans
+Instrument Sans
+Instrument Serif
+</reflex_fonts_to_reject>
+
+Reject every font that appears in the reflex_fonts_to_reject list. They are your training-data defaults and they create monoculture across projects.
+
+Step 3. Browse a font catalog with the 3 brand words in mind. Sources: Google Fonts, Pangram Pangram, Future Fonts, Adobe Fonts, ABC Dinamo, Klim Type Foundry, Velvetyne. Look for something that fits the brand as a *physical object* — a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a children's book printed on cheap newsprint. Reject the first thing that "looks designy" — that's the trained reflex too. Keep looking.
+
+Step 4. Cross-check the result. The right font for an "elegant" brief is NOT necessarily a serif. The right font for a "technical" brief is NOT necessarily a sans-serif. The right font for a "warm" brief is NOT Fraunces. If your final pick lines up with your reflex pattern, go back to Step 3.
+</font_selection_procedure>
+
+<typography_rules>
+DO use a modular type scale with fluid sizing (clamp) on headings.
+DO vary font weights and sizes to create clear visual hierarchy.
+DO vary your font choices across projects. If you used a serif display font on the last project, look for a sans, monospace, or display face on this one.
+
+DO NOT use overused fonts like Inter, Roboto, Arial, Open Sans, or system defaults — but also do not simply switch to your second-favorite. Every font in the reflex_fonts_to_reject list above is banned. Look further.
+DO NOT use monospace typography as lazy shorthand for "technical/developer" vibes.
+DO NOT put large icons with rounded corners above every heading. They rarely add value and make sites look templated.
+DO NOT use only one font family for the entire page. Pair a distinctive display font with a refined body font.
+DO NOT use a flat type hierarchy where sizes are too close together. Aim for at least a 1.25 ratio between steps.
+DO NOT set long body passages in uppercase. Reserve all-caps for short labels and headings.
+</typography_rules>
+
+### Color & Theme
+→ *Consult [color reference](reference/color-and-contrast.md) for the deeper material on contrast, accessibility, and palette construction.*
+
+Commit to a cohesive palette. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
+
+<color_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use OKLCH, not HSL. OKLCH is perceptually uniform: equal steps in lightness *look* equal, which HSL does not deliver. As you move toward white or black, REDUCE chroma — high chroma at extreme lightness looks garish. A light blue at 85% lightness wants ~0.08 chroma, not the 0.15 of your base color.
+- Tint your neutrals toward your brand hue. Even a chroma of 0.005-0.01 is perceptible and creates subconscious cohesion between brand color and UI surfaces. The hue you tint toward should come from THIS brand, not from a "warm = friendly" or "cool = tech" formula. Pick the brand's actual hue first, then tint everything toward it.
+- The 60-30-10 rule is about visual *weight*, not pixel count. 60% neutral / surface, 30% secondary text and borders, 10% accent. Accents work BECAUSE they're rare. Overuse kills their power.
+</color_principles>
+
+<theme_selection>
+Theme (light vs dark) should be DERIVED from audience and viewing context, not picked from a default. Read the brief and ask: when is this product used, by whom, in what physical setting?
+
+- A perp DEX consumed during fast trading sessions → dark
+- A hospital portal consumed by anxious patients on phones late at night → light
+- A children's reading app → light
+- A vintage motorcycle forum where users sit in their garage at 9pm → dark
+- An observability dashboard for SREs in a dark office → dark
+- A wedding planning checklist for couples on a Sunday morning → light
+- A music player app for headphone listening at night → dark
+- A food magazine homepage browsed during a coffee break → light
+
+Do not default everything to light "to play it safe." Do not default everything to dark "to look cool." Both defaults are the lazy reflex. The correct theme is the one the actual user wants in their actual context.
+</theme_selection>
+
+<color_rules>
+DO use modern CSS color functions (oklch, color-mix, light-dark) for perceptually uniform, maintainable palettes.
+DO tint your neutrals toward your brand hue. Even a subtle hint creates subconscious cohesion.
+
+DO NOT use gray text on colored backgrounds; it looks washed out. Use a shade of the background color instead.
+DO NOT use pure black (#000) or pure white (#fff). Always tint; pure black/white never appears in nature.
+DO NOT use the AI color palette: cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds.
+DO NOT use gradient text for impact — see <absolute_bans> below for the strict definition. Solid colors only for text.
+DO NOT default to dark mode with glowing accents. It looks "cool" without requiring actual design decisions.
+DO NOT default to light mode "to be safe" either. The point is to choose, not to retreat to a safe option.
+</color_rules>
+
+### Layout & Space
+→ *Consult [spatial reference](reference/spatial-design.md) for the deeper material on grids, container queries, and optical adjustments.*
+
+Create visual rhythm through varied spacing, not the same padding everywhere. Embrace asymmetry and unexpected compositions. Break the grid intentionally for emphasis.
+
+<spatial_principles>
+Always apply these — do not consult a reference, just do them:
+
+- Use a 4pt spacing scale with semantic token names (`--space-sm`, `--space-md`), not pixel-named (`--spacing-8`). Scale: 4, 8, 12, 16, 24, 32, 48, 64, 96. 8pt is too coarse — you'll often want 12px between two values.
+- Use `gap` instead of margins for sibling spacing. It eliminates margin collapse and the cleanup hacks that come with it.
+- Vary spacing for hierarchy. A heading with extra space above it reads as more important — make use of that. Don't apply the same padding everywhere.
+- Self-adjusting grid pattern: `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` is the breakpoint-free responsive grid for card-style content.
+- Container queries are for components, viewport queries are for page layout. A card in a sidebar should adapt to the sidebar's width, not the viewport's.
+</spatial_principles>
+
+<spatial_rules>
+DO create visual rhythm through varied spacing: tight groupings, generous separations.
+DO use fluid spacing with clamp() that breathes on larger screens.
+DO use asymmetry and unexpected compositions; break the grid intentionally for emphasis.
+
+DO NOT wrap everything in cards. Not everything needs a container.
+DO NOT nest cards inside cards. Visual noise; flatten the hierarchy.
+DO NOT use identical card grids (same-sized cards with icon + heading + text, repeated endlessly).
+DO NOT use the hero metric layout template (big number, small label, supporting stats, gradient accent).
+DO NOT center everything. Left-aligned text with asymmetric layouts feels more designed.
+DO NOT use the same spacing everywhere. Without rhythm, layouts feel monotonous.
+DO NOT let body text wrap beyond ~80 characters per line. Add a max-width like 65–75ch so the eye can track easily.
+</spatial_rules>
+
+### Visual Details
+
+<absolute_bans>
+These CSS patterns are NEVER acceptable. They are the most recognizable AI design tells. Match-and-refuse: if you find yourself about to write any of these, stop and rewrite the element with a different structure entirely.
+
+BAN 1: Side-stripe borders on cards/list items/callouts/alerts
+  - PATTERN: `border-left:` or `border-right:` with width greater than 1px
+  - INCLUDES: hard-coded colors AND CSS variables
+  - FORBIDDEN: `border-left: 3px solid red`, `border-left: 4px solid #ff0000`, `border-left: 4px solid var(--color-warning)`, `border-left: 5px solid oklch(...)`, etc.
+  - WHY: this is the single most overused "design touch" in admin, dashboard, and medical UIs. It never looks intentional regardless of color, radius, opacity, or whether the variable name is "primary" or "warning" or "accent."
+  - REWRITE: use a different element structure entirely. Do not just swap to box-shadow inset. Reach for full borders, background tints, leading numbers/icons, or no visual indicator at all.
+
+BAN 2: Gradient text
+  - PATTERN: `background-clip: text` (or `-webkit-background-clip: text`) combined with a gradient background
+  - FORBIDDEN: any combination that makes text fill come from a `linear-gradient`, `radial-gradient`, or `conic-gradient`
+  - WHY: gradient text is decorative rather than meaningful and is one of the top three AI design tells
+  - REWRITE: use a single solid color for text. If you want emphasis, use weight or size, not gradient fill.
+</absolute_bans>
+
+DO: Use intentional, purposeful decorative elements that reinforce brand.
+DO NOT: Use border-left or border-right greater than 1px as a colored accent stripe on cards, list items, callouts, or alerts. See <absolute_bans> above for the strict CSS pattern.
+DO NOT: Use glassmorphism everywhere (blur effects, glass cards, glow borders used decoratively rather than purposefully).
+DO NOT: Use sparklines as decoration. Tiny charts that look sophisticated but convey nothing meaningful.
+DO NOT: Use rounded rectangles with generic drop shadows. Safe, forgettable, could be any AI output.
+DO NOT: Use modals unless there's truly no better alternative. Modals are lazy.
+
+### Motion
+→ *Consult [motion reference](reference/motion-design.md) for timing, easing, and reduced motion.*
+
+Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
+
+**DO**: Use motion to convey state changes: entrances, exits, feedback
+**DO**: Use exponential easing (ease-out-quart/quint/expo) for natural deceleration
+**DO**: For height animations, use grid-template-rows transitions instead of animating height directly
+**DON'T**: Animate layout properties (width, height, padding, margin). Use transform and opacity only
+**DON'T**: Use bounce or elastic easing. They feel dated and tacky; real objects decelerate smoothly
+
+### Interaction
+→ *Consult [interaction reference](reference/interaction-design.md) for forms, focus, and loading patterns.*
+
+Make interactions feel fast. Use optimistic UI: update immediately, sync later.
+
+**DO**: Use progressive disclosure. Start simple, reveal sophistication through interaction (basic options first, advanced behind expandable sections; hover states that reveal secondary actions)
+**DO**: Design empty states that teach the interface, not just say "nothing here"
+**DO**: Make every interactive surface feel intentional and responsive
+**DON'T**: Repeat the same information (redundant headers, intros that restate the heading)
+**DON'T**: Make every button primary. Use ghost buttons, text links, secondary styles; hierarchy matters
+
+### Responsive
+→ *Consult [responsive reference](reference/responsive-design.md) for mobile-first, fluid design, and container queries.*
+
+**DO**: Use container queries (@container) for component-level responsiveness
+**DO**: Adapt the interface for different contexts, not just shrink it
+**DON'T**: Hide critical functionality on mobile. Adapt the interface, don't amputate it
+
+### UX Writing
+→ *Consult [ux-writing reference](reference/ux-writing.md) for labels, errors, and empty states.*
+
+**DO**: Make every word earn its place
+**DON'T**: Repeat information users can already see
+
+---
+
+## The AI Slop Test
+
+**Critical quality check**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
+
+A distinctive interface should make someone ask "how was this made?" not "which AI made this?"
+
+Review the DON'T guidelines above. They are the fingerprints of AI-generated work from 2024-2025.
+
+---
+
+## Implementation Principles
+
+Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
+
+Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices across generations.
+
+Remember: {{model}} is capable of extraordinary creative work. Don't hold back. Show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+
+---
+
+## Craft Mode
+
+If this skill is invoked with the argument "craft" (e.g., `{{command_prefix}}impeccable craft [feature description]`), follow the [craft flow](reference/craft.md). Pass any additional arguments as the feature description.
+
+---
+
+## Teach Mode
+
+If this skill is invoked with the argument "teach" (e.g., `{{command_prefix}}impeccable teach`), skip all design work above and instead run the teach flow below. This is a one-time setup that gathers design context for the project.
+
+### Step 1: Explore the Codebase
+
+Before asking questions, thoroughly scan the project to discover what you can:
+
+- **README and docs**: Project purpose, target audience, any stated goals
+- **Package.json / config files**: Tech stack, dependencies, existing design libraries
+- **Existing components**: Current design patterns, spacing, typography in use
+- **Brand assets**: Logos, favicons, color values already defined
+- **Design tokens / CSS variables**: Existing color palettes, font stacks, spacing scales
+- **Any style guides or brand documentation**
+
+Note what you've learned and what remains unclear.
+
+### Step 2: Ask UX-Focused Questions
+
+{{ask_instruction}} Focus only on what you couldn't infer from the codebase:
+
+#### Users & Purpose
+- Who uses this? What's their context when using it?
+- What job are they trying to get done?
+- What emotions should the interface evoke? (confidence, delight, calm, urgency, etc.)
+
+#### Brand & Personality
+- How would you describe the brand personality in 3 words?
+- Any reference sites or apps that capture the right feel? What specifically about them?
+- What should this explicitly NOT look like? Any anti-references?
+
+#### Aesthetic Preferences
+- Any strong preferences for visual direction? (minimal, bold, elegant, playful, technical, organic, etc.)
+- Light mode, dark mode, or both?
+- Any colors that must be used or avoided?
+
+#### Accessibility & Inclusion
+- Specific accessibility requirements? (WCAG level, known user needs)
+- Considerations for reduced motion, color blindness, or other accommodations?
+
+Skip questions where the answer is already clear from the codebase exploration.
+
+### Step 3: Write Design Context
+
+Synthesize your findings and the user's answers into a `## Design Context` section:
+
+```markdown
+## Design Context
+
+### Users
+[Who they are, their context, the job to be done]
+
+### Brand Personality
+[Voice, tone, 3-word personality, emotional goals]
+
+### Aesthetic Direction
+[Visual tone, references, anti-references, theme]
+
+### Design Principles
+[3-5 principles derived from the conversation that should guide all design decisions]
+```
+
+Write this section to `.impeccable.md` in the project root. If the file already exists, update the Design Context section in place.
+
+Then {{ask_instruction}} whether they'd also like the Design Context appended to {{config_file}}. If yes, append or update the section there as well.
+
+Confirm completion and summarize the key design principles that will now guide all future work.
+
+---
+
+## Extract Mode
+
+If this skill is invoked with the argument "extract" (e.g., `{{command_prefix}}impeccable extract [target]`), follow the [extract flow](reference/extract.md). Pass any additional arguments as the extraction target.

source/skills/impeccable/reference/color-and-contrast.md 🔗

@@ -0,0 +1,105 @@
+# Color & Contrast
+
+## Color Spaces: Use OKLCH
+
+**Stop using HSL.** Use OKLCH (or LCH) instead. It's perceptually uniform, meaning equal steps in lightness *look* equal—unlike HSL where 50% lightness in yellow looks bright while 50% in blue looks dark.
+
+The OKLCH function takes three components: `oklch(lightness chroma hue)` where lightness is 0-100%, chroma is roughly 0-0.4, and hue is 0-360. To build a primary color and its lighter / darker variants, hold the chroma+hue roughly constant and vary the lightness — but **reduce chroma as you approach white or black**, because high chroma at extreme lightness looks garish.
+
+The hue you pick is a brand decision and should not come from a default. Do not reach for blue (hue 250) or warm orange (hue 60) by reflex — those are the dominant AI-design defaults, not the right answer for any specific brand.
+
+## Building Functional Palettes
+
+### Tinted Neutrals
+
+**Pure gray is dead.** A neutral with zero chroma feels lifeless next to a colored brand. Add a tiny chroma value (0.005-0.015) to all your neutrals, hued toward whatever your brand color is. The chroma is small enough not to read as "tinted" consciously, but it creates subconscious cohesion between brand color and UI surfaces.
+
+The hue you tint toward should come from THIS project's brand, not from a "warm = friendly, cool = tech" formula. If your brand color is teal, your neutrals lean toward teal. If your brand color is amber, they lean toward amber. The point is cohesion with the SPECIFIC brand, not a stock palette.
+
+**Avoid** the trap of always tinting toward warm orange or always tinting toward cool blue. Those are the two laziest defaults and they create their own monoculture across projects.
+
+### Palette Structure
+
+A complete system needs:
+
+| Role | Purpose | Example |
+|------|---------|---------|
+| **Primary** | Brand, CTAs, key actions | 1 color, 3-5 shades |
+| **Neutral** | Text, backgrounds, borders | 9-11 shade scale |
+| **Semantic** | Success, error, warning, info | 4 colors, 2-3 shades each |
+| **Surface** | Cards, modals, overlays | 2-3 elevation levels |
+
+**Skip secondary/tertiary unless you need them.** Most apps work fine with one accent color. Adding more creates decision fatigue and visual noise.
+
+### The 60-30-10 Rule (Applied Correctly)
+
+This rule is about **visual weight**, not pixel count:
+
+- **60%**: Neutral backgrounds, white space, base surfaces
+- **30%**: Secondary colors—text, borders, inactive states
+- **10%**: Accent—CTAs, highlights, focus states
+
+The common mistake: using the accent color everywhere because it's "the brand color." Accent colors work *because* they're rare. Overuse kills their power.
+
+## Contrast & Accessibility
+
+### WCAG Requirements
+
+| Content Type | AA Minimum | AAA Target |
+|--------------|------------|------------|
+| Body text | 4.5:1 | 7:1 |
+| Large text (18px+ or 14px bold) | 3:1 | 4.5:1 |
+| UI components, icons | 3:1 | 4.5:1 |
+| Non-essential decorations | None | None |
+
+**The gotcha**: Placeholder text still needs 4.5:1. That light gray placeholder you see everywhere? Usually fails WCAG.
+
+### Dangerous Color Combinations
+
+These commonly fail contrast or cause readability issues:
+
+- Light gray text on white (the #1 accessibility fail)
+- **Gray text on any colored background**—gray looks washed out and dead on color. Use a darker shade of the background color, or transparency
+- Red text on green background (or vice versa)—8% of men can't distinguish these
+- Blue text on red background (vibrates visually)
+- Yellow text on white (almost always fails)
+- Thin light text on images (unpredictable contrast)
+
+### Never Use Pure Gray or Pure Black
+
+Pure gray (`oklch(50% 0 0)`) and pure black (`#000`) don't exist in nature—real shadows and surfaces always have a color cast. Even a chroma of 0.005-0.01 is enough to feel natural without being obviously tinted. (See tinted neutrals example above.)
+
+### Testing
+
+Don't trust your eyes. Use tools:
+
+- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
+- Browser DevTools → Rendering → Emulate vision deficiencies
+- [Polypane](https://polypane.app/) for real-time testing
+
+## Theming: Light & Dark Mode
+
+### Dark Mode Is Not Inverted Light Mode
+
+You can't just swap colors. Dark mode requires different design decisions:
+
+| Light Mode | Dark Mode |
+|------------|-----------|
+| Shadows for depth | Lighter surfaces for depth (no shadows) |
+| Dark text on light | Light text on dark (reduce font weight) |
+| Vibrant accents | Desaturate accents slightly |
+| White backgrounds | Never pure black—use dark gray (oklch 12-18%) |
+
+In dark mode, depth comes from surface lightness, not shadow. Build a 3-step surface scale where higher elevations are lighter (e.g. 15% / 20% / 25% lightness). Use the SAME hue and chroma as your brand color (whatever it is for THIS project — do not reach for blue) and only vary the lightness. Reduce body text weight slightly (e.g. 350 instead of 400) because light text on dark reads as heavier than dark text on light.
+
+### Token Hierarchy
+
+Use two layers: primitive tokens (`--blue-500`) and semantic tokens (`--color-primary: var(--blue-500)`). For dark mode, only redefine the semantic layer—primitives stay the same.
+
+## Alpha Is A Design Smell
+
+Heavy use of transparency (rgba, hsla) usually means an incomplete palette. Alpha creates unpredictable contrast, performance overhead, and inconsistency. Define explicit overlay colors for each context instead. Exception: focus rings and interactive states where see-through is needed.
+
+---
+
+**Avoid**: Relying on color alone to convey information. Creating palettes without clear roles for each color. Using pure black (#000) for large areas. Skipping color blindness testing (8% of men affected).

source/skills/impeccable/reference/craft.md 🔗

@@ -0,0 +1,70 @@
+# Craft Flow
+
+Build a feature with impeccable UX and UI quality through a structured process: shape the design, load the right references, then build and iterate visually until the result is delightful.
+
+## Step 1: Shape the Design
+
+Run {{command_prefix}}shape, passing along whatever feature description the user provided.
+
+Wait for the design brief to be fully confirmed before proceeding. The brief is your blueprint, and every implementation decision should trace back to it.
+
+If the user has already run {{command_prefix}}shape and has a confirmed design brief, skip this step and use the existing brief.
+
+## Step 2: Load References
+
+Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
+
+- [spatial-design.md](spatial-design.md) for layout and spacing
+- [typography.md](typography.md) for type hierarchy
+
+Then add references based on the brief's needs:
+- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
+- Animation or transitions? Consult [motion-design.md](motion-design.md)
+- Color-heavy or themed? Consult [color-and-contrast.md](color-and-contrast.md)
+- Responsive requirements? Consult [responsive-design.md](responsive-design.md)
+- Heavy on copy, labels, or errors? Consult [ux-writing.md](ux-writing.md)
+
+## Step 3: Build
+
+Implement the feature following the design brief. Work in this order:
+
+1. **Structure first**: HTML/semantic structure for the primary state. No styling yet.
+2. **Layout and spacing**: Establish the spatial rhythm and visual hierarchy.
+3. **Typography and color**: Apply the type scale and color system.
+4. **Interactive states**: Hover, focus, active, disabled.
+5. **Edge case states**: Empty, loading, error, overflow, first-run.
+6. **Motion**: Purposeful transitions and animations (if appropriate).
+7. **Responsive**: Adapt for different viewports. Don't just shrink; redesign for the context.
+
+### During Build
+- Test with real (or realistic) data at every step, not placeholder text
+- Check each state as you build it, not all at the end
+- If you discover a design question, stop and ask rather than guessing
+- Every visual choice should trace back to something in the design brief
+
+## Step 4: Visual Iteration
+
+**This step is critical.** Do not stop after the first implementation pass.
+
+Open the result in a browser window. If browser automation tools are available, use them to navigate to the page and visually inspect the result. If not, ask the user to open it and provide feedback.
+
+Iterate through these checks visually:
+
+1. **Does it match the brief?** Compare the live result against every section of the design brief. Fix discrepancies.
+2. **Does it pass the AI slop test?** If someone saw this and said "AI made this," would they believe it immediately? If yes, it needs more design intention.
+3. **Check against impeccable's DON'T guidelines.** Fix any anti-pattern violations.
+4. **Check every state.** Navigate through empty, error, loading, and edge case states. Each one should feel intentional, not like an afterthought.
+5. **Check responsive.** Resize the viewport. Does it adapt well or just shrink?
+6. **Check the details.** Spacing consistency, type hierarchy clarity, color contrast, interactive feedback, motion timing.
+
+After each round of fixes, visually verify again. **Repeat until you would be proud to show this to the user.** The bar is not "it works"; the bar is "this delights."
+
+## Step 5: Present
+
+Present the result to the user:
+- Show the feature in its primary state
+- Walk through the key states (empty, error, responsive)
+- Explain design decisions that connect back to the design brief
+- Ask: "What's working? What isn't?"
+
+Iterate based on feedback. Good design is rarely right on the first pass.

source/skills/impeccable/reference/extract.md 🔗

@@ -0,0 +1,70 @@
+# Extract Flow
+
+Identify reusable patterns, components, and design tokens, then extract and consolidate them into the design system for systematic reuse.
+
+## Step 1: Discover the Design System
+
+Find the design system, component library, or shared UI directory. Understand its structure: component organization, naming conventions, design token structure, import/export conventions.
+
+**CRITICAL**: If no design system exists, {{ask_instruction}} before creating one. Understand the preferred location and structure first.
+
+## Step 2: Identify Patterns
+
+Look for extraction opportunities in the target area:
+
+- **Repeated components**: Similar UI patterns used 3+ times (buttons, cards, inputs)
+- **Hard-coded values**: Colors, spacing, typography, shadows that should be tokens
+- **Inconsistent variations**: Multiple implementations of the same concept
+- **Composition patterns**: Layout or interaction patterns that repeat (form rows, toolbar groups, empty states)
+- **Type styles**: Repeated font-size + weight + line-height combinations
+- **Animation patterns**: Repeated easing, duration, or keyframe combinations
+
+Assess value: only extract things used 3+ times with the same intent. Premature abstraction is worse than duplication.
+
+## Step 3: Plan Extraction
+
+Create a systematic plan:
+
+- **Components to extract**: Which UI elements become reusable components?
+- **Tokens to create**: Which hard-coded values become design tokens?
+- **Variants to support**: What variations does each component need?
+- **Naming conventions**: Component names, token names, prop names that match existing patterns
+- **Migration path**: How to refactor existing uses to consume the new shared versions
+
+**IMPORTANT**: Design systems grow incrementally. Extract what is clearly reusable now, not everything that might someday be reusable.
+
+## Step 4: Extract & Enrich
+
+Build improved, reusable versions:
+
+- **Components**: Clear props API with sensible defaults, proper variants for different use cases, accessibility built in (ARIA, keyboard navigation, focus management), documentation and usage examples
+- **Design tokens**: Clear naming (primitive vs semantic), proper hierarchy and organization, documentation of when to use each token
+- **Patterns**: When to use this pattern, code examples, variations and combinations
+
+## Step 5: Migrate
+
+Replace existing uses with the new shared versions:
+
+- **Find all instances**: Search for the patterns you extracted
+- **Replace systematically**: Update each use to consume the shared version
+- **Test thoroughly**: Ensure visual and functional parity
+- **Delete dead code**: Remove the old implementations
+
+## Step 6: Document
+
+Update design system documentation:
+
+- Add new components to the component library
+- Document token usage and values
+- Add examples and guidelines
+- Update any Storybook or component catalog
+
+**NEVER**:
+- Extract one-off, context-specific implementations without generalization
+- Create components so generic they are useless
+- Extract without considering existing design system conventions
+- Skip proper TypeScript types or prop documentation
+- Create tokens for every single value (tokens should have semantic meaning)
+- Extract things that differ in intent (two buttons that look similar but serve different purposes should stay separate)
+
+Remember: A good design system is a living system. Extract patterns as they emerge, enrich them thoughtfully, and maintain them consistently.

source/skills/impeccable/reference/interaction-design.md 🔗

@@ -0,0 +1,195 @@
+# Interaction Design
+
+## The Eight Interactive States
+
+Every interactive element needs these states designed:
+
+| State | When | Visual Treatment |
+|-------|------|------------------|
+| **Default** | At rest | Base styling |
+| **Hover** | Pointer over (not touch) | Subtle lift, color shift |
+| **Focus** | Keyboard/programmatic focus | Visible ring (see below) |
+| **Active** | Being pressed | Pressed in, darker |
+| **Disabled** | Not interactive | Reduced opacity, no pointer |
+| **Loading** | Processing | Spinner, skeleton |
+| **Error** | Invalid state | Red border, icon, message |
+| **Success** | Completed | Green check, confirmation |
+
+**The common miss**: Designing hover without focus, or vice versa. They're different. Keyboard users never see hover states.
+
+## Focus Rings: Do Them Right
+
+**Never `outline: none` without replacement.** It's an accessibility violation. Instead, use `:focus-visible` to show focus only for keyboard users:
+
+```css
+/* Hide focus ring for mouse/touch */
+button:focus {
+  outline: none;
+}
+
+/* Show focus ring for keyboard */
+button:focus-visible {
+  outline: 2px solid var(--color-accent);
+  outline-offset: 2px;
+}
+```
+
+**Focus ring design**:
+- High contrast (3:1 minimum against adjacent colors)
+- 2-3px thick
+- Offset from element (not inside it)
+- Consistent across all interactive elements
+
+## Form Design: The Non-Obvious
+
+**Placeholders aren't labels**—they disappear on input. Always use visible `<label>` elements. **Validate on blur**, not on every keystroke (exception: password strength). Place errors **below** fields with `aria-describedby` connecting them.
+
+## Loading States
+
+**Optimistic updates**: Show success immediately, rollback on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. **Skeleton screens > spinners**—they preview content shape and feel faster than generic spinners.
+
+## Modals: The Inert Approach
+
+Focus trapping in modals used to require complex JavaScript. Now use the `inert` attribute:
+
+```html
+<!-- When modal is open -->
+<main inert>
+  <!-- Content behind modal can't be focused or clicked -->
+</main>
+<dialog open>
+  <h2>Modal Title</h2>
+  <!-- Focus stays inside modal -->
+</dialog>
+```
+
+Or use the native `<dialog>` element:
+
+```javascript
+const dialog = document.querySelector('dialog');
+dialog.showModal();  // Opens with focus trap, closes on Escape
+```
+
+## The Popover API
+
+For tooltips, dropdowns, and non-modal overlays, use native popovers:
+
+```html
+<button popovertarget="menu">Open menu</button>
+<div id="menu" popover>
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+**Benefits**: Light-dismiss (click outside closes), proper stacking, no z-index wars, accessible by default.
+
+## Dropdown & Overlay Positioning
+
+Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code.
+
+### CSS Anchor Positioning
+
+The modern solution uses the CSS Anchor Positioning API to tether an overlay to its trigger without JavaScript:
+
+```css
+.trigger {
+  anchor-name: --menu-trigger;
+}
+
+.dropdown {
+  position: fixed;
+  position-anchor: --menu-trigger;
+  position-area: block-end span-inline-end;
+  margin-top: 4px;
+}
+
+/* Flip above if no room below */
+@position-try --flip-above {
+  position-area: block-start span-inline-end;
+  margin-bottom: 4px;
+}
+```
+
+Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari - use a fallback for those browsers.
+
+### Popover + Anchor Combo
+
+Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern:
+
+```html
+<button popovertarget="menu" class="trigger">Open</button>
+<div id="menu" popover class="dropdown">
+  <button>Option 1</button>
+  <button>Option 2</button>
+</div>
+```
+
+The `popover` attribute places the element in the **top layer**, which sits above all other content regardless of z-index or overflow. No portal needed.
+
+### Portal / Teleport Pattern
+
+In component frameworks, render the dropdown at the document root and position it with JavaScript:
+
+- **React**: `createPortal(dropdown, document.body)`
+- **Vue**: `<Teleport to="body">`
+- **Svelte**: Use a portal library or mount to `document.body`
+
+Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
+
+### Fixed Positioning Fallback
+
+For browsers without anchor positioning support, `position: fixed` with manual coordinates avoids overflow clipping:
+
+```css
+.dropdown {
+  position: fixed;
+  /* top/left set via JS from trigger's getBoundingClientRect() */
+}
+```
+
+Check viewport boundaries before rendering. If the dropdown would overflow the bottom edge, flip it above the trigger. If it would overflow the right edge, align it to the trigger's right side instead.
+
+### Anti-Patterns
+
+- **`position: absolute` inside `overflow: hidden`** - The dropdown will be clipped. Use `position: fixed` or the top layer instead.
+- **Arbitrary z-index values** like `z-index: 9999` - Use a semantic z-index scale: `dropdown (100) -> sticky (200) -> modal-backdrop (300) -> modal (400) -> toast (500) -> tooltip (600)`.
+- **Rendering dropdown markup inline** without an escape hatch from the parent's stacking context. Either use `popover` (top layer), a portal, or `position: fixed`.
+
+## Destructive Actions: Undo > Confirm
+
+**Undo is better than confirmation dialogs**—users click through confirmations mindlessly. Remove from UI immediately, show undo toast, actually delete after toast expires. Use confirmation only for truly irreversible actions (account deletion), high-cost actions, or batch operations.
+
+## Keyboard Navigation Patterns
+
+### Roving Tabindex
+
+For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within:
+
+```html
+<div role="tablist">
+  <button role="tab" tabindex="0">Tab 1</button>
+  <button role="tab" tabindex="-1">Tab 2</button>
+  <button role="tab" tabindex="-1">Tab 3</button>
+</div>
+```
+
+Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
+
+### Skip Links
+
+Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus.
+
+## Gesture Discoverability
+
+Swipe-to-delete and similar gestures are invisible. Hint at their existence:
+
+- **Partially reveal**: Show delete button peeking from edge
+- **Onboarding**: Coach marks on first use
+- **Alternative**: Always provide a visible fallback (menu with "Delete")
+
+Don't rely on gestures as the only way to perform actions.
+
+---
+
+**Avoid**: Removing focus indicators without alternatives. Using placeholder text as labels. Touch targets <44x44px. Generic error messages. Custom controls without ARIA/keyboard support.

source/skills/impeccable/reference/motion-design.md 🔗

@@ -0,0 +1,99 @@
+# Motion Design
+
+## Duration: The 100/300/500 Rule
+
+Timing matters more than easing. These durations feel right for most UI:
+
+| Duration | Use Case | Examples |
+|----------|----------|----------|
+| **100-150ms** | Instant feedback | Button press, toggle, color change |
+| **200-300ms** | State changes | Menu open, tooltip, hover states |
+| **300-500ms** | Layout changes | Accordion, modal, drawer |
+| **500-800ms** | Entrance animations | Page load, hero reveals |
+
+**Exit animations are faster than entrances**—use ~75% of enter duration.
+
+## Easing: Pick the Right Curve
+
+**Don't use `ease`.** It's a compromise that's rarely optimal. Instead:
+
+| Curve | Use For | CSS |
+|-------|---------|-----|
+| **ease-out** | Elements entering | `cubic-bezier(0.16, 1, 0.3, 1)` |
+| **ease-in** | Elements leaving | `cubic-bezier(0.7, 0, 0.84, 0)` |
+| **ease-in-out** | State toggles (there → back) | `cubic-bezier(0.65, 0, 0.35, 1)` |
+
+**For micro-interactions, use exponential curves**—they feel natural because they mimic real physics (friction, deceleration):
+
+```css
+/* Quart out - smooth, refined (recommended default) */
+--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
+
+/* Quint out - slightly more dramatic */
+--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
+
+/* Expo out - snappy, confident */
+--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
+```
+
+**Avoid bounce and elastic curves.** They were trendy in 2015 but now feel tacky and amateurish. Real objects don't bounce when they stop—they decelerate smoothly. Overshoot effects draw attention to the animation itself rather than the content.
+
+## The Only Two Properties You Should Animate
+
+**transform** and **opacity** only—everything else causes layout recalculation. For height animations (accordions), use `grid-template-rows: 0fr → 1fr` instead of animating `height` directly.
+
+## Staggered Animations
+
+Use CSS custom properties for cleaner stagger: `animation-delay: calc(var(--i, 0) * 50ms)` with `style="--i: 0"` on each item. **Cap total stagger time**—10 items at 50ms = 500ms total. For many items, reduce per-item delay or cap staggered count.
+
+## Reduced Motion
+
+This is not optional. Vestibular disorders affect ~35% of adults over 40.
+
+```css
+/* Define animations normally */
+.card {
+  animation: slide-up 500ms ease-out;
+}
+
+/* Provide alternative for reduced motion */
+@media (prefers-reduced-motion: reduce) {
+  .card {
+    animation: fade-in 200ms ease-out;  /* Crossfade instead of motion */
+  }
+}
+
+/* Or disable entirely */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    transition-duration: 0.01ms !important;
+  }
+}
+```
+
+**What to preserve**: Functional animations like progress bars, loading spinners (slowed down), and focus indicators should still work—just without spatial movement.
+
+## Perceived Performance
+
+**Nobody cares how fast your site is—just how fast it feels.** Perception can be as effective as actual performance.
+
+**The 80ms threshold**: Our brains buffer sensory input for ~80ms to synchronize perception. Anything under 80ms feels instant and simultaneous. This is your target for micro-interactions.
+
+**Active vs passive time**: Passive waiting (staring at a spinner) feels longer than active engagement. Strategies to shift the balance:
+
+- **Preemptive start**: Begin transitions immediately while loading (iOS app zoom, skeleton UI). Users perceive work happening.
+- **Early completion**: Show content progressively—don't wait for everything. Video buffering, progressive images, streaming HTML.
+- **Optimistic UI**: Update the interface immediately, handle failures gracefully. Instagram likes work offline—the UI updates instantly, syncs later. Use for low-stakes actions; avoid for payments or destructive operations.
+
+**Easing affects perceived duration**: Ease-in (accelerating toward completion) makes tasks feel shorter because the peak-end effect weights final moments heavily. Ease-out feels satisfying for entrances, but ease-in toward a task's end compresses perceived time.
+
+**Caution**: Too-fast responses can decrease perceived value. Users may distrust instant results for complex operations (search, analysis). Sometimes a brief delay signals "real work" is happening.
+
+## Performance
+
+Don't use `will-change` preemptively—only when animation is imminent (`:hover`, `.animating`). For scroll-triggered animations, use Intersection Observer instead of scroll events; unobserve after animating once. Create motion tokens for consistency (durations, easings, common transitions).
+
+---
+
+**Avoid**: Animating everything (animation fatigue is real). Using >500ms for UI feedback. Ignoring `prefers-reduced-motion`. Using animation to hide slow loading.

source/skills/impeccable/reference/responsive-design.md 🔗

@@ -0,0 +1,114 @@
+# Responsive Design
+
+## Mobile-First: Write It Right
+
+Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.
+
+## Breakpoints: Content-Driven
+
+Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.
+
+## Detect Input Method, Not Just Screen Size
+
+**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:
+
+```css
+/* Fine pointer (mouse, trackpad) */
+@media (pointer: fine) {
+  .button { padding: 8px 16px; }
+}
+
+/* Coarse pointer (touch, stylus) */
+@media (pointer: coarse) {
+  .button { padding: 12px 20px; }  /* Larger touch target */
+}
+
+/* Device supports hover */
+@media (hover: hover) {
+  .card:hover { transform: translateY(-2px); }
+}
+
+/* Device doesn't support hover (touch) */
+@media (hover: none) {
+  .card { /* No hover state - use active instead */ }
+}
+```
+
+**Critical**: Don't rely on hover for functionality. Touch users can't hover.
+
+## Safe Areas: Handle the Notch
+
+Modern phones have notches, rounded corners, and home indicators. Use `env()`:
+
+```css
+body {
+  padding-top: env(safe-area-inset-top);
+  padding-bottom: env(safe-area-inset-bottom);
+  padding-left: env(safe-area-inset-left);
+  padding-right: env(safe-area-inset-right);
+}
+
+/* With fallback */
+.footer {
+  padding-bottom: max(1rem, env(safe-area-inset-bottom));
+}
+```
+
+**Enable viewport-fit** in your meta tag:
+```html
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
+```
+
+## Responsive Images: Get It Right
+
+### srcset with Width Descriptors
+
+```html
+<img
+  src="hero-800.jpg"
+  srcset="
+    hero-400.jpg 400w,
+    hero-800.jpg 800w,
+    hero-1200.jpg 1200w
+  "
+  sizes="(max-width: 768px) 100vw, 50vw"
+  alt="Hero image"
+>
+```
+
+**How it works**:
+- `srcset` lists available images with their actual widths (`w` descriptors)
+- `sizes` tells the browser how wide the image will display
+- Browser picks the best file based on viewport width AND device pixel ratio
+
+### Picture Element for Art Direction
+
+When you need different crops/compositions (not just resolutions):
+
+```html
+<picture>
+  <source media="(min-width: 768px)" srcset="wide.jpg">
+  <source media="(max-width: 767px)" srcset="tall.jpg">
+  <img src="fallback.jpg" alt="...">
+</picture>
+```
+
+## Layout Adaptation Patterns
+
+**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.
+
+## Testing: Don't Trust DevTools Alone
+
+DevTools device emulation is useful for layout but misses:
+
+- Actual touch interactions
+- Real CPU/memory constraints
+- Network latency patterns
+- Font rendering differences
+- Browser chrome/keyboard appearances
+
+**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.
+
+---
+
+**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

source/skills/impeccable/reference/spatial-design.md 🔗

@@ -0,0 +1,100 @@
+# Spatial Design
+
+## Spacing Systems
+
+### Use 4pt Base, Not 8pt
+
+8pt systems are too coarse—you'll frequently need 12px (between 8 and 16). Use 4pt for granularity: 4, 8, 12, 16, 24, 32, 48, 64, 96px.
+
+### Name Tokens Semantically
+
+Name by relationship (`--space-sm`, `--space-lg`), not value (`--spacing-8`). Use `gap` instead of margins for sibling spacing—it eliminates margin collapse and cleanup hacks.
+
+## Grid Systems
+
+### The Self-Adjusting Grid
+
+Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Columns are at least 280px, as many as fit per row, leftovers stretch. For complex layouts, use named grid areas (`grid-template-areas`) and redefine them at breakpoints.
+
+## Visual Hierarchy
+
+### The Squint Test
+
+Blur your eyes (or screenshot and blur). Can you still identify:
+- The most important element?
+- The second most important?
+- Clear groupings?
+
+If everything looks the same weight blurred, you have a hierarchy problem.
+
+### Hierarchy Through Multiple Dimensions
+
+Don't rely on size alone. Combine:
+
+| Tool | Strong Hierarchy | Weak Hierarchy |
+|------|------------------|----------------|
+| **Size** | 3:1 ratio or more | <2:1 ratio |
+| **Weight** | Bold vs Regular | Medium vs Regular |
+| **Color** | High contrast | Similar tones |
+| **Position** | Top/left (primary) | Bottom/right |
+| **Space** | Surrounded by white space | Crowded |
+
+**The best hierarchy uses 2-3 dimensions at once**: A heading that's larger, bolder, AND has more space above it.
+
+### Cards Are Not Required
+
+Cards are overused. Spacing and alignment create visual grouping naturally. Use cards only when content is truly distinct and actionable, items need visual comparison in a grid, or content needs clear interaction boundaries. **Never nest cards inside cards**—use spacing, typography, and subtle dividers for hierarchy within a card.
+
+## Container Queries
+
+Viewport queries are for page layouts. **Container queries are for components**:
+
+```css
+.card-container {
+  container-type: inline-size;
+}
+
+.card {
+  display: grid;
+  gap: var(--space-md);
+}
+
+/* Card layout changes based on its container, not viewport */
+@container (min-width: 400px) {
+  .card {
+    grid-template-columns: 120px 1fr;
+  }
+}
+```
+
+**Why this matters**: A card in a narrow sidebar stays compact, while the same card in a main content area expands—automatically, without viewport hacks.
+
+## Optical Adjustments
+
+Text at `margin-left: 0` looks indented due to letterform whitespace—use negative margin (`-0.05em`) to optically align. Geometrically centered icons often look off-center; play icons need to shift right, arrows shift toward their direction.
+
+### Touch Targets vs Visual Size
+
+Buttons can look small but need large touch targets (44px minimum). Use padding or pseudo-elements:
+
+```css
+.icon-button {
+  width: 24px;  /* Visual size */
+  height: 24px;
+  position: relative;
+}
+
+.icon-button::before {
+  content: '';
+  position: absolute;
+  inset: -10px;  /* Expand tap target to 44px */
+}
+```
+
+## Depth & Elevation
+
+Create semantic z-index scales (dropdown → sticky → modal-backdrop → modal → toast → tooltip) instead of arbitrary numbers. For shadows, create a consistent elevation scale (sm → md → lg → xl). **Key insight**: Shadows should be subtle—if you can clearly see it, it's probably too strong.
+
+---
+
+**Avoid**: Arbitrary spacing values outside your scale. Making all spacing equal (variety creates hierarchy). Creating hierarchy through size alone - combine size, weight, color, and space.

source/skills/impeccable/reference/typography.md 🔗

@@ -0,0 +1,142 @@
+# Typography
+
+## Classic Typography Principles
+
+### Vertical Rhythm
+
+Your line-height should be the base unit for ALL vertical spacing. If body text has `line-height: 1.5` on `16px` type (= 24px), spacing values should be multiples of 24px. This creates subconscious harmony—text and space share a mathematical foundation.
+
+### Modular Scale & Hierarchy
+
+The common mistake: too many font sizes that are too close together (14px, 15px, 16px, 18px...). This creates muddy hierarchy.
+
+**Use fewer sizes with more contrast.** A 5-size system covers most needs:
+
+| Role | Typical Ratio | Use Case |
+|------|---------------|----------|
+| xs | 0.75rem | Captions, legal |
+| sm | 0.875rem | Secondary UI, metadata |
+| base | 1rem | Body text |
+| lg | 1.25-1.5rem | Subheadings, lead text |
+| xl+ | 2-4rem | Headlines, hero text |
+
+Popular ratios: 1.25 (major third), 1.333 (perfect fourth), 1.5 (perfect fifth). Pick one and commit.
+
+### Readability & Measure
+
+Use `ch` units for character-based measure (`max-width: 65ch`). Line-height scales inversely with line length—narrow columns need tighter leading, wide columns need more.
+
+**Non-obvious**: Increase line-height for light text on dark backgrounds. The perceived weight is lighter, so text needs more breathing room. Add 0.05-0.1 to your normal line-height.
+
+## Font Selection & Pairing
+
+### Choosing Distinctive Fonts
+
+**Avoid the invisible defaults**: Inter, Roboto, Open Sans, Lato, Montserrat. These are everywhere, making your design feel generic. They're fine for documentation or tools where personality isn't the goal—but if you want distinctive design, look elsewhere.
+
+**Pick the font from the brief, not from a category preset.** The most common AI typography failure is reaching for the same "tasteful" font for every editorial brief, the same "modern" font for every tech brief, the same "elegant serif" for every premium brief. Those reflexes produce monoculture across projects. The right font is one whose physical character matches *this specific* brand, audience, and moment.
+
+A working selection process:
+
+1. Read the brief once. Write down three concrete words for the brand voice. Not "modern" or "elegant" — those are dead categories. Try "warm and mechanical and opinionated" or "calm and clinical and careful" or "fast and dense and unimpressed" or "handmade and a little weird."
+2. Now imagine the font as a physical object the brand could ship: a typewriter ribbon, a hand-lettered shop sign, a 1970s mainframe terminal manual, a fabric label on the inside of a coat, a museum exhibit caption, a tax form, a children's book printed on cheap newsprint. Whichever physical object fits the three words is pointing at the right *kind* of typeface.
+3. Browse a font catalog (Google Fonts, Pangram Pangram, Adobe Fonts, Future Fonts, ABC Dinamo) with that physical object in mind. **Reject the first thing that "looks designy."** That's your trained-everywhere reflex. Keep looking.
+4. Avoid your defaults from previous projects. If you find yourself reaching for the same display font you used last time, make yourself pick something else.
+
+**Anti-reflexes worth defending against**:
+- A technical/utilitarian brief does NOT need a serif "for warmth." Most tech tools should look like tech tools.
+- An editorial/premium brief does NOT need the same expressive serif everyone is using right now. Premium can be Swiss-modern, can be neo-grotesque, can be a literal monospace, can be a quiet humanist sans.
+- A children's product does NOT need a rounded display font. Kids' books use real type.
+- A "modern" brief does NOT need a geometric sans. The most modern thing you can do in 2026 is not use the font everyone else is using.
+
+**System fonts are underrated**: `-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui` looks native, loads instantly, and is highly readable. Consider this for apps where performance > personality.
+
+### Pairing Principles
+
+**The non-obvious truth**: You often don't need a second font. One well-chosen font family in multiple weights creates cleaner hierarchy than two competing typefaces. Only add a second font when you need genuine contrast (e.g., display headlines + body serif).
+
+When pairing, contrast on multiple axes:
+- Serif + Sans (structure contrast)
+- Geometric + Humanist (personality contrast)
+- Condensed display + Wide body (proportion contrast)
+
+**Never pair fonts that are similar but not identical** (e.g., two geometric sans-serifs). They create visual tension without clear hierarchy.
+
+### Web Font Loading
+
+The layout shift problem: fonts load late, text reflows, and users see content jump. Here's the fix:
+
+```css
+/* 1. Use font-display: swap for visibility */
+@font-face {
+  font-family: 'CustomFont';
+  src: url('font.woff2') format('woff2');
+  font-display: swap;
+}
+
+/* 2. Match fallback metrics to minimize shift */
+@font-face {
+  font-family: 'CustomFont-Fallback';
+  src: local('Arial');
+  size-adjust: 105%;        /* Scale to match x-height */
+  ascent-override: 90%;     /* Match ascender height */
+  descent-override: 20%;    /* Match descender depth */
+  line-gap-override: 10%;   /* Match line spacing */
+}
+
+body {
+  font-family: 'CustomFont', 'CustomFont-Fallback', sans-serif;
+}
+```
+
+Tools like [Fontaine](https://github.com/unjs/fontaine) calculate these overrides automatically.
+
+## Modern Web Typography
+
+### Fluid Type
+
+Fluid typography via `clamp(min, preferred, max)` scales text smoothly with the viewport. The middle value (e.g., `5vw + 1rem`) controls scaling rate—higher vw = faster scaling. Add a rem offset so it doesn't collapse to 0 on small screens.
+
+**Use fluid type for**: Headings and display text on marketing/content pages where text dominates the layout and needs to breathe across viewport sizes.
+
+**Use fixed `rem` scales for**: App UIs, dashboards, and data-dense interfaces. No major app design system (Material, Polaris, Primer, Carbon) uses fluid type in product UI — fixed scales with optional breakpoint adjustments give the spatial predictability that container-based layouts need. Body text should also be fixed even on marketing pages, since the size difference across viewports is too small to warrant it.
+
+### OpenType Features
+
+Most developers don't know these exist. Use them for polish:
+
+```css
+/* Tabular numbers for data alignment */
+.data-table { font-variant-numeric: tabular-nums; }
+
+/* Proper fractions */
+.recipe-amount { font-variant-numeric: diagonal-fractions; }
+
+/* Small caps for abbreviations */
+abbr { font-variant-caps: all-small-caps; }
+
+/* Disable ligatures in code */
+code { font-variant-ligatures: none; }
+
+/* Enable kerning (usually on by default, but be explicit) */
+body { font-kerning: normal; }
+```
+
+Check what features your font supports at [Wakamai Fondue](https://wakamaifondue.com/).
+
+## Typography System Architecture
+
+Name tokens semantically (`--text-body`, `--text-heading`), not by value (`--font-size-16`). Include font stacks, size scale, weights, line-heights, and letter-spacing in your token system.
+
+## Accessibility Considerations
+
+Beyond contrast ratios (which are well-documented), consider:
+
+- **Never disable zoom**: `user-scalable=no` breaks accessibility. If your layout breaks at 200% zoom, fix the layout.
+- **Use rem/em for font sizes**: This respects user browser settings. Never `px` for body text.
+- **Minimum 16px body text**: Smaller than this strains eyes and fails WCAG on mobile.
+- **Adequate touch targets**: Text links need padding or line-height that creates 44px+ tap targets.
+
+---
+
+**Avoid**: More than 2-3 font families per project. Skipping fallback font definitions. Ignoring font loading performance (FOUT/FOIT). Using decorative fonts for body text.

source/skills/impeccable/reference/ux-writing.md 🔗

@@ -0,0 +1,107 @@
+# UX Writing
+
+## The Button Label Problem
+
+**Never use "OK", "Submit", or "Yes/No".** These are lazy and ambiguous. Use specific verb + object patterns:
+
+| Bad | Good | Why |
+|-----|------|-----|
+| OK | Save changes | Says what will happen |
+| Submit | Create account | Outcome-focused |
+| Yes | Delete message | Confirms the action |
+| Cancel | Keep editing | Clarifies what "cancel" means |
+| Click here | Download PDF | Describes the destination |
+
+**For destructive actions**, name the destruction:
+- "Delete" not "Remove" (delete is permanent, remove implies recoverable)
+- "Delete 5 items" not "Delete selected" (show the count)
+
+## Error Messages: The Formula
+
+Every error message should answer: (1) What happened? (2) Why? (3) How to fix it? Example: "Email address isn't valid. Please include an @ symbol." not "Invalid input".
+
+### Error Message Templates
+
+| Situation | Template |
+|-----------|----------|
+| **Format error** | "[Field] needs to be [format]. Example: [example]" |
+| **Missing required** | "Please enter [what's missing]" |
+| **Permission denied** | "You don't have access to [thing]. [What to do instead]" |
+| **Network error** | "We couldn't reach [thing]. Check your connection and [action]." |
+| **Server error** | "Something went wrong on our end. We're looking into it. [Alternative action]" |
+
+### Don't Blame the User
+
+Reframe errors: "Please enter a date in MM/DD/YYYY format" not "You entered an invalid date".
+
+## Empty States Are Opportunities
+
+Empty states are onboarding moments: (1) Acknowledge briefly, (2) Explain the value of filling it, (3) Provide a clear action. "No projects yet. Create your first one to get started." not just "No items".
+
+## Voice vs Tone
+
+**Voice** is your brand's personality—consistent everywhere.
+**Tone** adapts to the moment.
+
+| Moment | Tone Shift |
+|--------|------------|
+| Success | Celebratory, brief: "Done! Your changes are live." |
+| Error | Empathetic, helpful: "That didn't work. Here's what to try..." |
+| Loading | Reassuring: "Saving your work..." |
+| Destructive confirm | Serious, clear: "Delete this project? This can't be undone." |
+
+**Never use humor for errors.** Users are already frustrated. Be helpful, not cute.
+
+## Writing for Accessibility
+
+**Link text** must have standalone meaning—"View pricing plans" not "Click here". **Alt text** describes information, not the image—"Revenue increased 40% in Q4" not "Chart". Use `alt=""` for decorative images. **Icon buttons** need `aria-label` for screen reader context.
+
+## Writing for Translation
+
+### Plan for Expansion
+
+German text is ~30% longer than English. Allocate space:
+
+| Language | Expansion |
+|----------|-----------|
+| German | +30% |
+| French | +20% |
+| Finnish | +30-40% |
+| Chinese | -30% (fewer chars, but same width) |
+
+### Translation-Friendly Patterns
+
+Keep numbers separate ("New messages: 3" not "You have 3 new messages"). Use full sentences as single strings (word order varies by language). Avoid abbreviations ("5 minutes ago" not "5 mins ago"). Give translators context about where strings appear.
+
+## Consistency: The Terminology Problem
+
+Pick one term and stick with it:
+
+| Inconsistent | Consistent |
+|--------------|------------|
+| Delete / Remove / Trash | Delete |
+| Settings / Preferences / Options | Settings |
+| Sign in / Log in / Enter | Sign in |
+| Create / Add / New | Create |
+
+Build a terminology glossary and enforce it. Variety creates confusion.
+
+## Avoid Redundant Copy
+
+If the heading explains it, the intro is redundant. If the button is clear, don't explain it again. Say it once, say it well.
+
+## Loading States
+
+Be specific: "Saving your draft..." not "Loading...". For long waits, set expectations ("This usually takes 30 seconds") or show progress.
+
+## Confirmation Dialogs: Use Sparingly
+
+Most confirmation dialogs are design failures—consider undo instead. When you must confirm: name the action, explain consequences, use specific button labels ("Delete project" / "Keep project", not "Yes" / "No").
+
+## Form Instructions
+
+Show format with placeholders, not instructions. For non-obvious fields, explain why you're asking.
+
+---
+
+**Avoid**: Jargon without explanation. Blaming users ("You made an error" → "This field is required"). Vague errors ("Something went wrong"). Varying terminology for variety. Humor for errors.

source/skills/impeccable/scripts/cleanup-deprecated.mjs 🔗

@@ -0,0 +1,214 @@
+#!/usr/bin/env node
+/**
+ * Cleans up deprecated Impeccable skill files, symlinks, and
+ * skills-lock.json entries left over from previous versions.
+ *
+ * Safe to run repeatedly -- it is a no-op when nothing needs cleaning.
+ *
+ * Usage (from the project root):
+ *   node {{scripts_path}}/cleanup-deprecated.mjs
+ *
+ * What it does:
+ *   1. Finds every harness-specific skills directory (.claude/skills,
+ *      .cursor/skills, .agents/skills, etc.).
+ *   2. For each deprecated skill name (with and without i- prefix),
+ *      checks if the directory exists and its SKILL.md mentions
+ *      "impeccable" (to avoid deleting unrelated user skills).
+ *   3. Deletes confirmed matches (files, directories, or symlinks).
+ *   4. Removes the corresponding entries from skills-lock.json.
+ */
+
+import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, unlinkSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+
+// Skills that were renamed, merged, or folded in v2.0 and v2.1.
+const DEPRECATED_NAMES = [
+  'frontend-design',    // renamed to impeccable (v2.0)
+  'teach-impeccable',   // folded into /impeccable teach (v2.0)
+  'arrange',            // renamed to layout (v2.1)
+  'normalize',          // merged into polish (v2.1)
+  'onboard',            // merged into harden (v2.1)
+  'extract',            // merged into /impeccable extract (v2.1)
+];
+
+// All known harness directories that may contain a skills/ subfolder.
+const HARNESS_DIRS = [
+  '.claude', '.cursor', '.gemini', '.codex', '.agents',
+  '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
+];
+
+/**
+ * Walk up from startDir until we find a directory that looks like a
+ * project root (has package.json, .git, or skills-lock.json).
+ */
+export function findProjectRoot(startDir = process.cwd()) {
+  let dir = resolve(startDir);
+  const { root } = { root: '/' };
+  while (dir !== root) {
+    if (
+      existsSync(join(dir, 'package.json')) ||
+      existsSync(join(dir, '.git')) ||
+      existsSync(join(dir, 'skills-lock.json'))
+    ) {
+      return dir;
+    }
+    const parent = resolve(dir, '..');
+    if (parent === dir) break;
+    dir = parent;
+  }
+  return resolve(startDir);
+}
+
+/**
+ * Check whether a skill directory belongs to Impeccable by reading its
+ * SKILL.md and looking for the word "impeccable" (case-insensitive).
+ * Returns false for non-existent paths or skills that don't match.
+ */
+export function isImpeccableSkill(skillDir) {
+  const skillMd = join(skillDir, 'SKILL.md');
+  if (!existsSync(skillMd)) return false;
+  try {
+    const content = readFileSync(skillMd, 'utf-8');
+    return /impeccable/i.test(content);
+  } catch {
+    return false;
+  }
+}
+
+/**
+ * Build the full list of names to check: each deprecated name, plus
+ * its i-prefixed variant.
+ */
+export function buildTargetNames() {
+  const names = [];
+  for (const name of DEPRECATED_NAMES) {
+    names.push(name);
+    names.push(`i-${name}`);
+  }
+  return names;
+}
+
+/**
+ * Find every skills directory across all harness dirs in the project.
+ * Returns absolute paths that exist on disk.
+ */
+export function findSkillsDirs(projectRoot) {
+  const dirs = [];
+  for (const harness of HARNESS_DIRS) {
+    const candidate = join(projectRoot, harness, 'skills');
+    if (existsSync(candidate)) {
+      dirs.push(candidate);
+    }
+  }
+  return dirs;
+}
+
+/**
+ * Remove deprecated skill directories/symlinks from all harness dirs.
+ * Returns an array of paths that were deleted.
+ */
+export function removeDeprecatedSkills(projectRoot) {
+  const targets = buildTargetNames();
+  const skillsDirs = findSkillsDirs(projectRoot);
+  const deleted = [];
+
+  for (const skillsDir of skillsDirs) {
+    for (const name of targets) {
+      const skillPath = join(skillsDir, name);
+
+      // Use lstat to detect symlinks (existsSync follows symlinks and
+      // returns false for dangling ones).
+      let stat;
+      try {
+        stat = lstatSync(skillPath);
+      } catch {
+        continue; // does not exist at all
+      }
+
+      if (stat.isSymbolicLink()) {
+        // Symlink: check the target if it's alive, otherwise treat
+        // dangling symlinks to deprecated names as safe to remove.
+        const targetAlive = existsSync(skillPath);
+        const isMatch = targetAlive ? isImpeccableSkill(skillPath) : true;
+        if (isMatch) {
+          unlinkSync(skillPath);
+          deleted.push(skillPath);
+        }
+        continue;
+      }
+
+      // Regular directory -- verify it belongs to impeccable
+      if (isImpeccableSkill(skillPath)) {
+        rmSync(skillPath, { recursive: true, force: true });
+        deleted.push(skillPath);
+      }
+    }
+  }
+
+  return deleted;
+}
+
+/**
+ * Remove deprecated entries from skills-lock.json.
+ * Only removes entries whose source is "pbakaus/impeccable".
+ * Returns the list of removed skill names.
+ */
+export function cleanSkillsLock(projectRoot) {
+  const lockPath = join(projectRoot, 'skills-lock.json');
+  if (!existsSync(lockPath)) return [];
+
+  let lock;
+  try {
+    lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
+  } catch {
+    return [];
+  }
+
+  if (!lock.skills || typeof lock.skills !== 'object') return [];
+
+  const targets = buildTargetNames();
+  const removed = [];
+
+  for (const name of targets) {
+    const entry = lock.skills[name];
+    if (!entry) continue;
+    // Only remove if it belongs to impeccable
+    if (entry.source === 'pbakaus/impeccable') {
+      delete lock.skills[name];
+      removed.push(name);
+    }
+  }
+
+  if (removed.length > 0) {
+    writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8');
+  }
+
+  return removed;
+}
+
+/**
+ * Run the full cleanup. Returns a summary object.
+ */
+export function cleanup(projectRoot) {
+  const root = projectRoot || findProjectRoot();
+  const deletedPaths = removeDeprecatedSkills(root);
+  const removedLockEntries = cleanSkillsLock(root);
+  return { deletedPaths, removedLockEntries, projectRoot: root };
+}
+
+// CLI entry point
+if (process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname)) {
+  const result = cleanup();
+  if (result.deletedPaths.length === 0 && result.removedLockEntries.length === 0) {
+    console.log('No deprecated Impeccable skills found. Nothing to clean up.');
+  } else {
+    if (result.deletedPaths.length > 0) {
+      console.log(`Removed ${result.deletedPaths.length} deprecated skill(s):`);
+      for (const p of result.deletedPaths) console.log(`  - ${p}`);
+    }
+    if (result.removedLockEntries.length > 0) {
+      console.log(`Cleaned ${result.removedLockEntries.length} entry/entries from skills-lock.json:`);
+      for (const name of result.removedLockEntries) console.log(`  - ${name}`);
+    }
+  }
+}

source/skills/layout/SKILL.md 🔗

@@ -0,0 +1,124 @@
+---
+name: layout
+description: "Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first.
+
+---
+
+## Assess Current Layout
+
+Analyze what's weak about the current spatial design:
+
+1. **Spacing**:
+   - Is spacing consistent or arbitrary? (Random padding/margin values)
+   - Is all spacing the same? (Equal padding everywhere = no rhythm)
+   - Are related elements grouped tightly, with generous space between groups?
+
+2. **Visual hierarchy**:
+   - Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
+   - Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
+   - Does whitespace guide the eye to what matters?
+
+3. **Grid & structure**:
+   - Is there a clear underlying structure, or does the layout feel random?
+   - Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
+   - Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
+
+4. **Rhythm & variety**:
+   - Does the layout have visual rhythm? (Alternating tight/generous spacing)
+   - Is every section structured the same way? (Monotonous repetition)
+   - Are there intentional moments of surprise or emphasis?
+
+5. **Density**:
+   - Is the layout too cramped? (Not enough breathing room)
+   - Is the layout too sparse? (Excessive whitespace without purpose)
+   - Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
+
+**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
+
+## Plan Layout Improvements
+
+Consult the [spatial design reference](reference/spatial-design.md) from the impeccable skill for detailed guidance on grids, rhythm, and container queries.
+
+Create a systematic plan:
+
+- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
+- **Hierarchy strategy**: How will space communicate importance?
+- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
+- **Rhythm**: Where should spacing be tight vs generous?
+
+## Improve Layout Systematically
+
+### Establish a Spacing System
+
+- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
+- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
+- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
+- Apply `clamp()` for fluid spacing that breathes on larger screens
+
+### Create Visual Rhythm
+
+- **Tight grouping** for related elements (8-12px between siblings)
+- **Generous separation** between distinct sections (48-96px)
+- **Varied spacing** within sections — not every row needs the same gap
+- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
+
+### Choose the Right Layout Tool
+
+- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
+- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
+- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
+- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
+- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
+
+### Break Card Grid Monotony
+
+- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
+- Use cards only when content is truly distinct and actionable — never nest cards inside cards
+- Vary card sizes, span columns, or mix cards with non-card content to break repetition
+
+### Strengthen Visual Hierarchy
+
+- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
+- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
+- Create clear content groupings through proximity and separation.
+
+### Manage Depth & Elevation
+
+- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
+- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
+- Use elevation to reinforce hierarchy, not as decoration
+
+### Optical Adjustments
+
+- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
+
+**NEVER**:
+- Use arbitrary spacing values outside your scale
+- Make all spacing equal — variety creates hierarchy
+- Wrap everything in cards — not everything needs a container
+- Nest cards inside cards — use spacing and dividers for hierarchy within
+- Use identical card grids everywhere (icon + heading + text, repeated)
+- Center everything — left-aligned with asymmetry feels more designed
+- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
+- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
+- Use arbitrary z-index values (999, 9999) — build a semantic scale
+
+## Verify Layout Improvements
+
+- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
+- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
+- **Hierarchy**: Is the most important content obvious within 2 seconds?
+- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
+- **Consistency**: Is the spacing system applied uniformly?
+- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
+
+Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.

source/skills/optimize/SKILL.md 🔗

@@ -0,0 +1,266 @@
+---
+name: optimize
+description: "Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Identify and fix performance issues to create faster, smoother user experiences.
+
+## Assess Performance Issues
+
+Understand current performance and identify problems:
+
+1. **Measure current state**:
+   - **Core Web Vitals**: LCP, FID/INP, CLS scores
+   - **Load time**: Time to interactive, first contentful paint
+   - **Bundle size**: JavaScript, CSS, image sizes
+   - **Runtime performance**: Frame rate, memory usage, CPU usage
+   - **Network**: Request count, payload sizes, waterfall
+
+2. **Identify bottlenecks**:
+   - What's slow? (Initial load? Interactions? Animations?)
+   - What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
+   - How bad is it? (Perceivable? Annoying? Blocking?)
+   - Who's affected? (All users? Mobile only? Slow connections?)
+
+**CRITICAL**: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
+
+## Optimization Strategy
+
+Create systematic improvement plan:
+
+### Loading Performance
+
+**Optimize Images**:
+- Use modern formats (WebP, AVIF)
+- Proper sizing (don't load 3000px image for 300px display)
+- Lazy loading for below-fold images
+- Responsive images (`srcset`, `picture` element)
+- Compress images (80-85% quality is usually imperceptible)
+- Use CDN for faster delivery
+
+```html
+<img 
+  src="hero.webp"
+  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
+  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
+  loading="lazy"
+  alt="Hero image"
+/>
+```
+
+**Reduce JavaScript Bundle**:
+- Code splitting (route-based, component-based)
+- Tree shaking (remove unused code)
+- Remove unused dependencies
+- Lazy load non-critical code
+- Use dynamic imports for large components
+
+```javascript
+// Lazy load heavy component
+const HeavyChart = lazy(() => import('./HeavyChart'));
+```
+
+**Optimize CSS**:
+- Remove unused CSS
+- Critical CSS inline, rest async
+- Minimize CSS files
+- Use CSS containment for independent regions
+
+**Optimize Fonts**:
+- Use `font-display: swap` or `optional`
+- Subset fonts (only characters you need)
+- Preload critical fonts
+- Use system fonts when appropriate
+- Limit font weights loaded
+
+```css
+@font-face {
+  font-family: 'CustomFont';
+  src: url('/fonts/custom.woff2') format('woff2');
+  font-display: swap; /* Show fallback immediately */
+  unicode-range: U+0020-007F; /* Basic Latin only */
+}
+```
+
+**Optimize Loading Strategy**:
+- Critical resources first (async/defer non-critical)
+- Preload critical assets
+- Prefetch likely next pages
+- Service worker for offline/caching
+- HTTP/2 or HTTP/3 for multiplexing
+
+### Rendering Performance
+
+**Avoid Layout Thrashing**:
+```javascript
+// ❌ Bad: Alternating reads and writes (causes reflows)
+elements.forEach(el => {
+  const height = el.offsetHeight; // Read (forces layout)
+  el.style.height = height * 2; // Write
+});
+
+// ✅ Good: Batch reads, then batch writes
+const heights = elements.map(el => el.offsetHeight); // All reads
+elements.forEach((el, i) => {
+  el.style.height = heights[i] * 2; // All writes
+});
+```
+
+**Optimize Rendering**:
+- Use CSS `contain` property for independent regions
+- Minimize DOM depth (flatter is faster)
+- Reduce DOM size (fewer elements)
+- Use `content-visibility: auto` for long lists
+- Virtual scrolling for very long lists (react-window, react-virtualized)
+
+**Reduce Paint & Composite**:
+- Use `transform` and `opacity` for animations (GPU-accelerated)
+- Avoid animating layout properties (width, height, top, left)
+- Use `will-change` sparingly for known expensive operations
+- Minimize paint areas (smaller is faster)
+
+### Animation Performance
+
+**GPU Acceleration**:
+```css
+/* ✅ GPU-accelerated (fast) */
+.animated {
+  transform: translateX(100px);
+  opacity: 0.5;
+}
+
+/* ❌ CPU-bound (slow) */
+.animated {
+  left: 100px;
+  width: 300px;
+}
+```
+
+**Smooth 60fps**:
+- Target 16ms per frame (60fps)
+- Use `requestAnimationFrame` for JS animations
+- Debounce/throttle scroll handlers
+- Use CSS animations when possible
+- Avoid long-running JavaScript during animations
+
+**Intersection Observer**:
+```javascript
+// Efficiently detect when elements enter viewport
+const observer = new IntersectionObserver((entries) => {
+  entries.forEach(entry => {
+    if (entry.isIntersecting) {
+      // Element is visible, lazy load or animate
+    }
+  });
+});
+```
+
+### React/Framework Optimization
+
+**React-specific**:
+- Use `memo()` for expensive components
+- `useMemo()` and `useCallback()` for expensive computations
+- Virtualize long lists
+- Code split routes
+- Avoid inline function creation in render
+- Use React DevTools Profiler
+
+**Framework-agnostic**:
+- Minimize re-renders
+- Debounce expensive operations
+- Memoize computed values
+- Lazy load routes and components
+
+### Network Optimization
+
+**Reduce Requests**:
+- Combine small files
+- Use SVG sprites for icons
+- Inline small critical assets
+- Remove unused third-party scripts
+
+**Optimize APIs**:
+- Use pagination (don't load everything)
+- GraphQL to request only needed fields
+- Response compression (gzip, brotli)
+- HTTP caching headers
+- CDN for static assets
+
+**Optimize for Slow Connections**:
+- Adaptive loading based on connection (navigator.connection)
+- Optimistic UI updates
+- Request prioritization
+- Progressive enhancement
+
+## Core Web Vitals Optimization
+
+### Largest Contentful Paint (LCP < 2.5s)
+- Optimize hero images
+- Inline critical CSS
+- Preload key resources
+- Use CDN
+- Server-side rendering
+
+### First Input Delay (FID < 100ms) / INP (< 200ms)
+- Break up long tasks
+- Defer non-critical JavaScript
+- Use web workers for heavy computation
+- Reduce JavaScript execution time
+
+### Cumulative Layout Shift (CLS < 0.1)
+- Set dimensions on images and videos
+- Don't inject content above existing content
+- Use `aspect-ratio` CSS property
+- Reserve space for ads/embeds
+- Avoid animations that cause layout shifts
+
+```css
+/* Reserve space for image */
+.image-container {
+  aspect-ratio: 16 / 9;
+}
+```
+
+## Performance Monitoring
+
+**Tools to use**:
+- Chrome DevTools (Lighthouse, Performance panel)
+- WebPageTest
+- Core Web Vitals (Chrome UX Report)
+- Bundle analyzers (webpack-bundle-analyzer)
+- Performance monitoring (Sentry, DataDog, New Relic)
+
+**Key metrics**:
+- LCP, FID/INP, CLS (Core Web Vitals)
+- Time to Interactive (TTI)
+- First Contentful Paint (FCP)
+- Total Blocking Time (TBT)
+- Bundle size
+- Request count
+
+**IMPORTANT**: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
+
+**NEVER**:
+- Optimize without measuring (premature optimization)
+- Sacrifice accessibility for performance
+- Break functionality while optimizing
+- Use `will-change` everywhere (creates new layers, uses memory)
+- Lazy load above-fold content
+- Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
+- Forget about mobile performance (often slower devices, slower connections)
+
+## Verify Improvements
+
+Test that optimizations worked:
+
+- **Before/after metrics**: Compare Lighthouse scores
+- **Real user monitoring**: Track improvements for real users
+- **Different devices**: Test on low-end Android, not just flagship iPhone
+- **Slow connections**: Throttle to 3G, test experience
+- **No regressions**: Ensure functionality still works
+- **User perception**: Does it *feel* faster?
+
+Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.
+

source/skills/overdrive/SKILL.md 🔗

@@ -0,0 +1,141 @@
+---
+name: overdrive
+description: "Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Start your response with:
+
+```
+──────────── ⚡ OVERDRIVE ─────────────
+》》》 Entering overdrive mode...
+```
+
+Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first.
+
+**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
+
+### Propose Before Building
+
+This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
+
+1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
+2. **{{ask_instruction}}** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
+3. Only proceed with the direction the user confirms.
+
+Skipping this step risks building something embarrassing that needs to be thrown away.
+
+### Iterate with Browser Automation
+
+Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
+
+---
+
+## Assess What "Extraordinary" Means Here
+
+The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
+
+### For visual/marketing surfaces
+Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
+
+### For functional UI
+Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
+
+### For performance-critical UI
+The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
+
+### For data-heavy interfaces
+Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
+
+**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
+
+## The Toolkit
+
+Organized by what you're trying to achieve, not by technology name.
+
+### Make transitions feel cinematic
+- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
+- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
+- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
+
+### Tie animation to scroll position
+- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
+
+### Render beyond CSS
+- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
+- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
+- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
+- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
+
+### Make data feel alive
+- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
+- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
+- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
+
+### Animate complex properties
+- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
+- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
+
+### Push performance boundaries
+- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
+- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
+- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
+
+### Interact with the device
+- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
+- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
+
+**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
+
+## Implement with Discipline
+
+### Progressive enhancement is non-negotiable
+
+Every technique must degrade gracefully. The experience without the enhancement must still be good.
+
+```css
+@supports (animation-timeline: scroll()) {
+  .hero { animation-timeline: scroll(); }
+}
+```
+
+```javascript
+if ('gpu' in navigator) { /* WebGPU */ }
+else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
+/* CSS-only fallback must still look good */
+```
+
+### Performance rules
+
+- Target 60fps. If dropping below 50, simplify.
+- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
+- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
+- Pause off-screen rendering. Kill what you can't see.
+- Test on real mid-range devices, not just your development machine.
+
+### Polish is the difference
+
+The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
+
+**NEVER**:
+- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
+- Ship effects that cause jank on mid-range devices
+- Use bleeding-edge APIs without a functional fallback
+- Add sound without explicit user opt-in
+- Use technical ambition to mask weak design fundamentals — fix those first with other skills
+- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
+
+## Verify the Result
+
+- **The wow test**: Show it to someone who hasn't seen it. Do they react?
+- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
+- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
+- **The accessibility test**: Enable reduced motion. Still beautiful?
+- **The context test**: Does this make sense for THIS brand and audience?
+
+Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.

source/skills/polish/SKILL.md 🔗

@@ -0,0 +1,224 @@
+---
+name: polish
+description: "Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first. Additionally gather: quality bar (MVP vs flagship).
+
+---
+
+Perform a meticulous final pass to catch all the small details that separate good work from great work. The difference between shipped and polished.
+
+## Design System Discovery
+
+Before polishing, understand the system you are polishing toward:
+
+1. **Find the design system**: Search for design system documentation, component libraries, style guides, or token definitions. Study the core patterns: color tokens, spacing scale, typography styles, component API.
+2. **Note the conventions**: How are shared components imported? What spacing scale is used? Which colors come from tokens vs hard-coded values? What motion and interaction patterns are established?
+3. **Identify drift**: Where does the target feature deviate from the system? Hard-coded values that should be tokens, custom components that duplicate shared ones, spacing that doesn't match the scale.
+
+If a design system exists, polish should align the feature with it. If none exists, polish against the conventions visible in the codebase.
+
+## Pre-Polish Assessment
+
+Understand the current state and goals:
+
+1. **Review completeness**:
+   - Is it functionally complete?
+   - Are there known issues to preserve (mark with TODOs)?
+   - What's the quality bar? (MVP vs flagship feature?)
+   - When does it ship? (How much time for polish?)
+
+2. **Identify polish areas**:
+   - Visual inconsistencies
+   - Spacing and alignment issues
+   - Interaction state gaps
+   - Copy inconsistencies
+   - Edge cases and error states
+   - Loading and transition smoothness
+
+**CRITICAL**: Polish is the last step, not the first. Don't polish work that's not functionally complete.
+
+## Polish Systematically
+
+Work through these dimensions methodically:
+
+### Visual Alignment & Spacing
+
+- **Pixel-perfect alignment**: Everything lines up to grid
+- **Consistent spacing**: All gaps use spacing scale (no random 13px gaps)
+- **Optical alignment**: Adjust for visual weight (icons may need offset for optical centering)
+- **Responsive consistency**: Spacing and alignment work at all breakpoints
+- **Grid adherence**: Elements snap to baseline grid
+
+**Check**:
+- Enable grid overlay and verify alignment
+- Check spacing with browser inspector
+- Test at multiple viewport sizes
+- Look for elements that "feel" off
+
+### Typography Refinement
+
+- **Hierarchy consistency**: Same elements use same sizes/weights throughout
+- **Line length**: 45-75 characters for body text
+- **Line height**: Appropriate for font size and context
+- **Widows & orphans**: No single words on last line
+- **Hyphenation**: Appropriate for language and column width
+- **Kerning**: Adjust letter spacing where needed (especially headlines)
+- **Font loading**: No FOUT/FOIT flashes
+
+### Color & Contrast
+
+- **Contrast ratios**: All text meets WCAG standards
+- **Consistent token usage**: No hard-coded colors, all use design tokens
+- **Theme consistency**: Works in all theme variants
+- **Color meaning**: Same colors mean same things throughout
+- **Accessible focus**: Focus indicators visible with sufficient contrast
+- **Tinted neutrals**: No pure gray or pure black—add subtle color tint (0.01 chroma)
+- **Gray on color**: Never put gray text on colored backgrounds—use a shade of that color or transparency
+
+### Interaction States
+
+Every interactive element needs all states:
+
+- **Default**: Resting state
+- **Hover**: Subtle feedback (color, scale, shadow)
+- **Focus**: Keyboard focus indicator (never remove without replacement)
+- **Active**: Click/tap feedback
+- **Disabled**: Clearly non-interactive
+- **Loading**: Async action feedback
+- **Error**: Validation or error state
+- **Success**: Successful completion
+
+**Missing states create confusion and broken experiences**.
+
+### Micro-interactions & Transitions
+
+- **Smooth transitions**: All state changes animated appropriately (150-300ms)
+- **Consistent easing**: Use ease-out-quart/quint/expo for natural deceleration. Never bounce or elastic—they feel dated.
+- **No jank**: 60fps animations, only animate transform and opacity
+- **Appropriate motion**: Motion serves purpose, not decoration
+- **Reduced motion**: Respects `prefers-reduced-motion`
+
+### Content & Copy
+
+- **Consistent terminology**: Same things called same names throughout
+- **Consistent capitalization**: Title Case vs Sentence case applied consistently
+- **Grammar & spelling**: No typos
+- **Appropriate length**: Not too wordy, not too terse
+- **Punctuation consistency**: Periods on sentences, not on labels (unless all labels have them)
+
+### Icons & Images
+
+- **Consistent style**: All icons from same family or matching style
+- **Appropriate sizing**: Icons sized consistently for context
+- **Proper alignment**: Icons align with adjacent text optically
+- **Alt text**: All images have descriptive alt text
+- **Loading states**: Images don't cause layout shift, proper aspect ratios
+- **Retina support**: 2x assets for high-DPI screens
+
+### Forms & Inputs
+
+- **Label consistency**: All inputs properly labeled
+- **Required indicators**: Clear and consistent
+- **Error messages**: Helpful and consistent
+- **Tab order**: Logical keyboard navigation
+- **Auto-focus**: Appropriate (don't overuse)
+- **Validation timing**: Consistent (on blur vs on submit)
+
+### Edge Cases & Error States
+
+- **Loading states**: All async actions have loading feedback
+- **Empty states**: Helpful empty states, not just blank space
+- **Error states**: Clear error messages with recovery paths
+- **Success states**: Confirmation of successful actions
+- **Long content**: Handles very long names, descriptions, etc.
+- **No content**: Handles missing data gracefully
+- **Offline**: Appropriate offline handling (if applicable)
+
+### Responsiveness
+
+- **All breakpoints**: Test mobile, tablet, desktop
+- **Touch targets**: 44x44px minimum on touch devices
+- **Readable text**: No text smaller than 14px on mobile
+- **No horizontal scroll**: Content fits viewport
+- **Appropriate reflow**: Content adapts logically
+
+### Performance
+
+- **Fast initial load**: Optimize critical path
+- **No layout shift**: Elements don't jump after load (CLS)
+- **Smooth interactions**: No lag or jank
+- **Optimized images**: Appropriate formats and sizes
+- **Lazy loading**: Off-screen content loads lazily
+
+### Code Quality
+
+- **Remove console logs**: No debug logging in production
+- **Remove commented code**: Clean up dead code
+- **Remove unused imports**: Clean up unused dependencies
+- **Consistent naming**: Variables and functions follow conventions
+- **Type safety**: No TypeScript `any` or ignored errors
+- **Accessibility**: Proper ARIA labels and semantic HTML
+
+## Polish Checklist
+
+Go through systematically:
+
+- [ ] Visual alignment perfect at all breakpoints
+- [ ] Spacing uses design tokens consistently
+- [ ] Typography hierarchy consistent
+- [ ] All interactive states implemented
+- [ ] All transitions smooth (60fps)
+- [ ] Copy is consistent and polished
+- [ ] Icons are consistent and properly sized
+- [ ] All forms properly labeled and validated
+- [ ] Error states are helpful
+- [ ] Loading states are clear
+- [ ] Empty states are welcoming
+- [ ] Touch targets are 44x44px minimum
+- [ ] Contrast ratios meet WCAG AA
+- [ ] Keyboard navigation works
+- [ ] Focus indicators visible
+- [ ] No console errors or warnings
+- [ ] No layout shift on load
+- [ ] Works in all supported browsers
+- [ ] Respects reduced motion preference
+- [ ] Code is clean (no TODOs, console.logs, commented code)
+
+**IMPORTANT**: Polish is about details. Zoom in. Squint at it. Use it yourself. The little things add up.
+
+**NEVER**:
+- Polish before it's functionally complete
+- Spend hours on polish if it ships in 30 minutes (triage)
+- Introduce bugs while polishing (test thoroughly)
+- Ignore systematic issues (if spacing is off everywhere, fix the system)
+- Perfect one thing while leaving others rough (consistent quality level)
+- Create new one-off components when design system equivalents exist
+- Hard-code values that should use design tokens
+
+## Final Verification
+
+Before marking as done:
+
+- **Use it yourself**: Actually interact with the feature
+- **Test on real devices**: Not just browser DevTools
+- **Ask someone else to review**: Fresh eyes catch things
+- **Compare to design**: Match intended design
+- **Check all states**: Don't just test happy path
+
+## Clean Up
+
+After polishing, ensure code quality:
+
+- **Replace custom implementations**: If the design system provides a component you reimplemented, switch to the shared version.
+- **Remove orphaned code**: Delete unused styles, components, or files made obsolete by polish.
+- **Consolidate tokens**: If you introduced new values, check whether they should be tokens.
+- **Verify DRYness**: Look for duplication introduced during polishing and consolidate.
+
+Remember: You have impeccable attention to detail and exquisite taste. Polish until it feels effortless, looks intentional, and works flawlessly. Sweat the details - they matter.
+

source/skills/quieter/SKILL.md 🔗

@@ -0,0 +1,102 @@
+---
+name: quieter
+description: "Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first.
+
+---
+
+## Assess Current State
+
+Analyze what makes the design feel too intense:
+
+1. **Identify intensity sources**:
+   - **Color saturation**: Overly bright or saturated colors
+   - **Contrast extremes**: Too much high-contrast juxtaposition
+   - **Visual weight**: Too many bold, heavy elements competing
+   - **Animation excess**: Too much motion or overly dramatic effects
+   - **Complexity**: Too many visual elements, patterns, or decorations
+   - **Scale**: Everything is large and loud with no hierarchy
+
+2. **Understand the context**:
+   - What's the purpose? (Marketing vs tool vs reading experience)
+   - Who's the audience? (Some contexts need energy)
+   - What's working? (Don't throw away good ideas)
+   - What's the core message? (Preserve what matters)
+
+If any of these are unclear from the codebase, {{ask_instruction}}
+
+**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
+
+## Plan Refinement
+
+Create a strategy to reduce intensity while maintaining impact:
+
+- **Color approach**: Desaturate or shift to more sophisticated tones?
+- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
+- **Simplification approach**: What can be removed entirely?
+- **Sophistication approach**: How can we signal quality through restraint?
+
+**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
+
+## Refine the Design
+
+Systematically reduce intensity across these dimensions:
+
+### Color Refinement
+- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
+- **Soften palette**: Replace bright colors with muted, sophisticated tones
+- **Reduce color variety**: Use fewer colors more thoughtfully
+- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
+- **Gentler contrasts**: High contrast only where it matters most
+- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
+- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
+
+### Visual Weight Reduction
+- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
+- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
+- **White space**: Increase breathing room, reduce density
+- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
+
+### Simplification
+- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
+- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
+- **Reduce layering**: Flatten visual hierarchy where possible
+- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
+
+### Motion Reduction
+- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
+- **Remove decorative animations**: Keep functional motion, remove flourishes
+- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
+- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
+- **Remove animations entirely** if they're not serving a clear purpose
+
+### Composition Refinement
+- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
+- **Align to grid**: Bring rogue elements back into systematic alignment
+- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
+
+**NEVER**:
+- Make everything the same size/weight (hierarchy still matters)
+- Remove all color (quiet ≠ grayscale)
+- Eliminate all personality (maintain character through refinement)
+- Sacrifice usability for aesthetics (functional elements still need clear affordances)
+- Make everything small and light (some anchors needed)
+
+## Verify Quality
+
+Ensure refinement maintains quality:
+
+- **Still functional**: Can users still accomplish tasks easily?
+- **Still distinctive**: Does it have character, or is it generic now?
+- **Better reading**: Is text easier to read for extended periods?
+- **Sophistication**: Does it feel more refined and premium?
+
+Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.

source/skills/shape/SKILL.md 🔗

@@ -0,0 +1,95 @@
+---
+name: shape
+description: "Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written."
+argument-hint: "[feature to shape]"
+user-invocable: true
+---
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable, which contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding. If no design context exists yet, you MUST run {{command_prefix}}impeccable teach first.
+
+---
+
+Shape the UX and UI for a feature before any code is written. This skill produces a **design brief**: a structured artifact that guides implementation through discovery, not guesswork.
+
+**Scope**: Design planning only. This skill does NOT write code. It produces the thinking that makes code good.
+
+**Output**: A design brief that can be handed off to {{command_prefix}}impeccable craft, {{command_prefix}}impeccable, or any other implementation skill.
+
+## Philosophy
+
+Most AI-generated UIs fail not because of bad code, but because of skipped thinking. They jump to "here's a card grid" without asking "what is the user trying to accomplish?" This skill inverts that: understand deeply first, so implementation is precise.
+
+## Phase 1: Discovery Interview
+
+**Do NOT write any code or make any design decisions during this phase.** Your only job is to understand the feature deeply enough to make excellent design decisions later.
+
+Ask these questions in conversation, adapting based on answers. Don't dump them all at once; have a natural dialogue. {{ask_instruction}}
+
+### Purpose & Context
+- What is this feature for? What problem does it solve?
+- Who specifically will use it? (Not "users"; be specific: role, context, frequency)
+- What does success look like? How will you know this feature is working?
+- What's the user's state of mind when they reach this feature? (Rushed? Exploring? Anxious? Focused?)
+
+### Content & Data
+- What content or data does this feature display or collect?
+- What are the realistic ranges? (Minimum, typical, maximum, e.g., 0 items, 5 items, 500 items)
+- What are the edge cases? (Empty state, error state, first-time use, power user)
+- Is any content dynamic? What changes and how often?
+
+### Design Goals
+- What's the single most important thing a user should do or understand here?
+- What should this feel like? (Fast/efficient? Calm/trustworthy? Fun/playful? Premium/refined?)
+- Are there existing patterns in the product this should be consistent with?
+- Are there specific examples (inside or outside the product) that capture what you're going for?
+
+### Constraints
+- Are there technical constraints? (Framework, performance budget, browser support)
+- Are there content constraints? (Localization, dynamic text length, user-generated content)
+- Mobile/responsive requirements?
+- Accessibility requirements beyond WCAG AA?
+
+### Anti-Goals
+- What should this NOT be? What would be a wrong direction?
+- What's the biggest risk of getting this wrong?
+
+## Phase 2: Design Brief
+
+After the interview, synthesize everything into a structured design brief. Present it to the user for confirmation before considering this skill complete.
+
+### Brief Structure
+
+**1. Feature Summary** (2-3 sentences)
+What this is, who it's for, what it needs to accomplish.
+
+**2. Primary User Action**
+The single most important thing a user should do or understand here.
+
+**3. Design Direction**
+How this should feel. What aesthetic approach fits. Reference the project's design context from `.impeccable.md` and explain how this feature should express it.
+
+**4. Layout Strategy**
+High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
+
+**5. Key States**
+List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
+
+**6. Interaction Model**
+How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
+
+**7. Content Requirements**
+What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges.
+
+**8. Recommended References**
+Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., spatial-design.md for complex layouts, motion-design.md for animated features, interaction-design.md for form-heavy features).
+
+**9. Open Questions**
+Anything unresolved that the implementer should resolve during build.
+
+---
+
+{{ask_instruction}} Get explicit confirmation of the brief before finishing. If the user disagrees with any part, revisit the relevant discovery questions.
+
+Once confirmed, the brief is complete. The user can now hand it to {{command_prefix}}impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use {{command_prefix}}impeccable craft instead, which runs this skill internally.)

source/skills/typeset/SKILL.md 🔗

@@ -0,0 +1,115 @@
+---
+name: typeset
+description: "Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography."
+argument-hint: "[target]"
+user-invocable: true
+---
+
+Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
+
+## MANDATORY PREPARATION
+
+Invoke {{command_prefix}}impeccable — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run {{command_prefix}}impeccable teach first.
+
+---
+
+## Assess Current Typography
+
+Analyze what's weak or generic about the current type:
+
+1. **Font choices**:
+   - Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
+   - Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
+   - Are there too many font families? (More than 2-3 is almost always a mess)
+
+2. **Hierarchy**:
+   - Can you tell headings from body from captions at a glance?
+   - Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
+   - Are weight contrasts strong enough? (Medium vs Regular is barely visible)
+
+3. **Sizing & scale**:
+   - Is there a consistent type scale, or are sizes arbitrary?
+   - Does body text meet minimum readability? (16px+)
+   - Is the sizing strategy appropriate for the context? (Fixed `rem` scales for app UIs; fluid `clamp()` for marketing/content page headings)
+
+4. **Readability**:
+   - Are line lengths comfortable? (45-75 characters ideal)
+   - Is line-height appropriate for the font and context?
+   - Is there enough contrast between text and background?
+
+5. **Consistency**:
+   - Are the same elements styled the same way throughout?
+   - Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
+   - Is letter-spacing intentional or default everywhere?
+
+**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
+
+## Plan Typography Improvements
+
+Consult the [typography reference](reference/typography.md) from the impeccable skill for detailed guidance on scales, pairing, and loading strategies.
+
+Create a systematic plan:
+
+- **Font selection**: Do fonts need replacing? What fits the brand/context?
+- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
+- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
+- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
+
+## Improve Typography Systematically
+
+### Font Selection
+
+If fonts need replacing:
+- Choose fonts that reflect the brand personality
+- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
+- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
+
+### Establish Hierarchy
+
+Build a clear type scale:
+- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
+- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
+- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
+- **App UIs**: Use a fixed `rem`-based type scale, optionally adjusted at 1-2 breakpoints. Fluid sizing undermines the spatial predictability that dense, container-based layouts need
+- **Marketing / content pages**: Use fluid sizing via `clamp(min, preferred, max)` for headings and display text. Keep body text fixed
+
+### Fix Readability
+
+- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
+- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
+- Increase line-height slightly for light-on-dark text
+- Ensure body text is at least 16px / 1rem
+
+### Refine Details
+
+- Use `tabular-nums` for data tables and numbers that should align
+- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
+- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
+- Set `font-kerning: normal` and consider OpenType features where appropriate
+
+### Weight Consistency
+
+- Define clear roles for each weight and stick to them
+- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
+- Load only the weights you actually use (each weight adds to page load)
+
+**NEVER**:
+- Use more than 2-3 font families
+- Pick sizes arbitrarily — commit to a scale
+- Set body text below 16px
+- Use decorative/display fonts for body text
+- Disable browser zoom (`user-scalable=no`)
+- Use `px` for font sizes — use `rem` to respect user settings
+- Default to Inter/Roboto/Open Sans when personality matters
+- Pair fonts that are similar but not identical (two geometric sans-serifs)
+
+## Verify Typography Improvements
+
+- **Hierarchy**: Can you identify heading vs body vs caption instantly?
+- **Readability**: Is body text comfortable to read in long passages?
+- **Consistency**: Are same-role elements styled identically throughout?
+- **Personality**: Does the typography reflect the brand?
+- **Performance**: Are web fonts loading efficiently without layout shift?
+- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
+
+Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.

src/detect-antipatterns-browser.js 🔗

@@ -0,0 +1,2402 @@
+/**
+ * Anti-Pattern Browser Detector for Impeccable
+ * Copyright (c) 2026 Paul Bakaus
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * GENERATED -- do not edit. Source: detect-antipatterns.mjs
+ * Rebuild: node scripts/build-browser-detector.js
+ *
+ * Usage: <script src="detect-antipatterns-browser.js"></script>
+ * Re-scan: window.impeccableScan()
+ */
+(function () {
+if (typeof window === 'undefined') return;
+
+/**
+ * Anti-Pattern Detector for Impeccable
+ * Copyright (c) 2026 Paul Bakaus
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Universal file — auto-detects environment (browser vs Node) and adapts.
+ *
+ * Node usage:
+ *   node detect-antipatterns.mjs [file-or-dir...]   # jsdom for HTML, regex for rest
+ *   node detect-antipatterns.mjs https://...         # Puppeteer (auto)
+ *   node detect-antipatterns.mjs --fast [files...]   # regex-only (skip jsdom)
+ *   node detect-antipatterns.mjs --json              # JSON output
+ *
+ * Browser usage:
+ *   <script src="detect-antipatterns-browser.js"></script>
+ *   Re-scan: window.impeccableScan()
+ *
+ * Exit codes: 0 = clean, 2 = findings
+ */
+
+// ─── Environment ────────────────────────────────────────────────────────────
+
+const IS_BROWSER = true;
+const IS_NODE = !IS_BROWSER;
+
+
+// ─── Section 1: Constants ───────────────────────────────────────────────────
+
+const SAFE_TAGS = new Set([
+  'blockquote', 'nav', 'a', 'input', 'textarea', 'select',
+  'pre', 'code', 'span', 'th', 'td', 'tr', 'li', 'label',
+  'button', 'hr', 'html', 'head', 'body', 'script', 'style',
+  'link', 'meta', 'title', 'br', 'img', 'svg', 'path', 'circle',
+  'rect', 'line', 'polyline', 'polygon', 'g', 'defs', 'use',
+]);
+
+// Per-check safe-tags override for the border (side-tab / border-accent)
+// rule. We intentionally re-allow <label> here because card-shaped clickable
+// labels (e.g. .checklist-item wrapping a checkbox + content) are one of the
+// canonical side-tab anti-pattern shapes and must be detected. The rule's
+// other preconditions (non-neutral color, width >= 2px on a single side,
+// radius > 0 or width >= 3, element size >= 20x20 in the browser path)
+// already filter out plain inline form labels so this does not introduce
+// false positives. See modern-color-borders.html for the test matrix.
+const BORDER_SAFE_TAGS = new Set(
+  [...SAFE_TAGS].filter(t => t !== 'label')
+);
+
+const OVERUSED_FONTS = new Set([
+  'inter', 'roboto', 'open sans', 'lato', 'montserrat', 'arial', 'helvetica',
+]);
+
+// Brand-associated fonts: don't flag these as "overused" on the brand's own domains.
+// Keys are font names, values are arrays of hostname suffixes where the font is allowed.
+const GOOGLE_DOMAINS = [
+  'google.com', 'youtube.com', 'android.com', 'chromium.org',
+  'chrome.com', 'web.dev', 'gstatic.com', 'firebase.google.com',
+];
+const BRAND_FONT_DOMAINS = {
+  'roboto': GOOGLE_DOMAINS,
+  'google sans': GOOGLE_DOMAINS,
+  'product sans': GOOGLE_DOMAINS,
+};
+
+function isBrandFontOnOwnDomain(font) {
+  if (typeof location === 'undefined') return false;
+  const allowed = BRAND_FONT_DOMAINS[font];
+  if (!allowed) return false;
+  const host = location.hostname.toLowerCase();
+  return allowed.some(suffix => host === suffix || host.endsWith('.' + suffix));
+}
+
+const GENERIC_FONTS = new Set([
+  'serif', 'sans-serif', 'monospace', 'cursive', 'fantasy',
+  'system-ui', 'ui-serif', 'ui-sans-serif', 'ui-monospace', 'ui-rounded',
+  '-apple-system', 'blinkmacsystemfont', 'segoe ui',
+  'inherit', 'initial', 'unset', 'revert',
+]);
+
+const ANTIPATTERNS = [
+  // ── AI slop: tells that something was AI-generated ──
+  {
+    id: 'side-tab',
+    category: 'slop',
+    name: 'Side-tab accent border',
+    description:
+      'Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.',
+    skillSection: 'Visual Details',
+    skillGuideline: 'colored accent stripe',
+  },
+  {
+    id: 'border-accent-on-rounded',
+    category: 'slop',
+    name: 'Border accent on rounded element',
+    description:
+      'Thick accent border on a rounded card — the border clashes with the rounded corners. Remove the border or the border-radius.',
+    skillSection: 'Visual Details',
+    skillGuideline: 'colored accent stripe',
+  },
+  {
+    id: 'overused-font',
+    category: 'slop',
+    name: 'Overused font',
+    description:
+      'Inter, Roboto, Open Sans, Lato, Montserrat, and Arial are used on millions of sites. Choose a distinctive font that gives your interface personality.',
+    skillSection: 'Typography',
+    skillGuideline: 'overused fonts like Inter',
+  },
+  {
+    id: 'single-font',
+    category: 'slop',
+    name: 'Single font for everything',
+    description:
+      'Only one font family is used for the entire page. Pair a distinctive display font with a refined body font to create typographic hierarchy.',
+    skillSection: 'Typography',
+    skillGuideline: 'only one font family for the entire page',
+  },
+  {
+    id: 'flat-type-hierarchy',
+    category: 'slop',
+    name: 'Flat type hierarchy',
+    description:
+      'Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).',
+    skillSection: 'Typography',
+    skillGuideline: 'flat type hierarchy',
+  },
+  {
+    id: 'gradient-text',
+    category: 'slop',
+    name: 'Gradient text',
+    description:
+      'Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'gradient text for',
+  },
+  {
+    id: 'ai-color-palette',
+    category: 'slop',
+    name: 'AI color palette',
+    description:
+      'Purple/violet gradients and cyan-on-dark are the most recognizable tells of AI-generated UIs. Choose a distinctive, intentional palette.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'AI color palette',
+  },
+  {
+    id: 'nested-cards',
+    category: 'slop',
+    name: 'Nested cards',
+    description:
+      'Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.',
+    skillSection: 'Layout & Space',
+    skillGuideline: 'Nest cards inside cards',
+  },
+  {
+    id: 'monotonous-spacing',
+    category: 'slop',
+    name: 'Monotonous spacing',
+    description:
+      'The same spacing value used everywhere — no rhythm, no variation. Use tight groupings for related items and generous separations between sections.',
+    skillSection: 'Layout & Space',
+    skillGuideline: 'same spacing everywhere',
+  },
+  {
+    id: 'everything-centered',
+    category: 'slop',
+    name: 'Everything centered',
+    description:
+      'Every text element is center-aligned. Left-aligned text with asymmetric layouts feels more designed. Center only hero sections and CTAs.',
+    skillSection: 'Layout & Space',
+    skillGuideline: 'Center everything',
+  },
+  {
+    id: 'bounce-easing',
+    category: 'slop',
+    name: 'Bounce or elastic easing',
+    description:
+      'Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.',
+    skillSection: 'Motion',
+    skillGuideline: 'bounce or elastic easing',
+  },
+  {
+    id: 'dark-glow',
+    category: 'slop',
+    name: 'Dark mode with glowing accents',
+    description:
+      'Dark backgrounds with colored box-shadow glows are the default "cool" look of AI-generated UIs. Use subtle, purposeful lighting instead — or skip the dark theme entirely.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'dark mode with glowing accents',
+  },
+  {
+    id: 'icon-tile-stack',
+    category: 'slop',
+    name: 'Icon tile stacked above heading',
+    description:
+      'A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.',
+    skillSection: 'Typography',
+    skillGuideline: 'large icons with rounded corners above every heading',
+  },
+
+  // ── Quality: general design and accessibility issues ──
+  {
+    id: 'pure-black-white',
+    category: 'quality',
+    name: 'Pure black background',
+    description:
+      'Pure #000000 as a background color looks harsh and unnatural. Tint it slightly toward your brand hue (e.g., oklch(12% 0.01 250)) for a more refined feel.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'pure black (#000)',
+  },
+  {
+    id: 'gray-on-color',
+    category: 'quality',
+    name: 'Gray text on colored background',
+    description:
+      'Gray text looks washed out on colored backgrounds. Use a darker shade of the background color instead, or white/near-white for contrast.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'gray text on colored backgrounds',
+  },
+  {
+    id: 'low-contrast',
+    category: 'quality',
+    name: 'Low contrast text',
+    description:
+      'Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.',
+  },
+  {
+    id: 'layout-transition',
+    category: 'quality',
+    name: 'Layout property animation',
+    description:
+      'Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.',
+    skillSection: 'Motion',
+    skillGuideline: 'Animate layout properties',
+  },
+  {
+    id: 'line-length',
+    category: 'quality',
+    name: 'Line length too long',
+    description:
+      'Text lines wider than ~80 characters are hard to read. The eye loses its place tracking back to the start of the next line. Add a max-width (65ch to 75ch) to text containers.',
+    skillSection: 'Layout & Space',
+    skillGuideline: 'wrap beyond ~80 characters',
+  },
+  {
+    id: 'cramped-padding',
+    category: 'quality',
+    name: 'Cramped padding',
+    description:
+      'Text is too close to the edge of its container. Add at least 8px (ideally 12-16px) of padding inside bordered or colored containers.',
+  },
+  {
+    id: 'tight-leading',
+    category: 'quality',
+    name: 'Tight line height',
+    description:
+      'Line height below 1.3x the font size makes multi-line text hard to read. Use 1.5 to 1.7 for body text so lines have room to breathe.',
+  },
+  {
+    id: 'skipped-heading',
+    category: 'quality',
+    name: 'Skipped heading level',
+    description:
+      'Heading levels should not skip (e.g. h1 then h3 with no h2). Screen readers use heading hierarchy for navigation. Skipping levels breaks the document outline.',
+  },
+  {
+    id: 'justified-text',
+    category: 'quality',
+    name: 'Justified text',
+    description:
+      'Justified text without hyphenation creates uneven word spacing ("rivers of white"). Use text-align: left for body text, or enable hyphens: auto if you must justify.',
+  },
+  {
+    id: 'tiny-text',
+    category: 'quality',
+    name: 'Tiny body text',
+    description:
+      'Body text below 12px is hard to read, especially on high-DPI screens. Use at least 14px for body content, 16px is ideal.',
+  },
+  {
+    id: 'all-caps-body',
+    category: 'quality',
+    name: 'All-caps body text',
+    description:
+      'Long passages in uppercase are hard to read. We recognize words by shape (ascenders and descenders), which all-caps removes. Reserve uppercase for short labels and headings.',
+    skillSection: 'Typography',
+    skillGuideline: 'long body passages in uppercase',
+  },
+  {
+    id: 'wide-tracking',
+    category: 'quality',
+    name: 'Wide letter spacing on body text',
+    description:
+      'Letter spacing above 0.05em on body text disrupts natural character groupings and slows reading. Reserve wide tracking for short uppercase labels only.',
+  },
+];
+
+// ─── Section 2: Color Utilities ─────────────────────────────────────────────
+
+function isNeutralColor(color) {
+  if (!color || color === 'transparent') return true;
+
+  // rgb/rgba — use channel spread. Threshold 30 ≈ 11.7% of the 0–255 range.
+  const rgb = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
+  if (rgb) {
+    return (Math.max(+rgb[1], +rgb[2], +rgb[3]) - Math.min(+rgb[1], +rgb[2], +rgb[3])) < 30;
+  }
+
+  // oklch()/lch() — chroma is the second numeric component.
+  // oklch chroma is ~0–0.4 in sRGB gamut; >= 0.02 reads as tinted, not gray.
+  // lch chroma is ~0–150; >= 3 reads as tinted. jsdom emits both formats
+  // literally (it does NOT convert them to rgb).
+  const oklch = color.match(/oklch\(\s*[\d.%-]+\s+([\d.-]+)/i);
+  if (oklch) return parseFloat(oklch[1]) < 0.02;
+  const lch = color.match(/lch\(\s*[\d.%-]+\s+([\d.-]+)/i);
+  if (lch) return parseFloat(lch[1]) < 3;
+
+  // oklab()/lab() — a and b are signed axes; chroma = sqrt(a² + b²).
+  // oklab a/b are ~-0.4..0.4, threshold 0.02. lab a/b are ~-128..127, threshold 3.
+  const oklab = color.match(/oklab\(\s*[\d.%-]+\s+([\d.-]+)\s+([\d.-]+)/i);
+  if (oklab) {
+    const a = parseFloat(oklab[1]), b = parseFloat(oklab[2]);
+    return Math.hypot(a, b) < 0.02;
+  }
+  const lab = color.match(/lab\(\s*[\d.%-]+\s+([\d.-]+)\s+([\d.-]+)/i);
+  if (lab) {
+    const a = parseFloat(lab[1]), b = parseFloat(lab[2]);
+    return Math.hypot(a, b) < 3;
+  }
+
+  // hsl/hsla — saturation is the second numeric component (percent).
+  // Modern jsdom usually converts hsl() to rgb, but handle it directly for
+  // safety across versions and for any engine that preserves the format.
+  const hsl = color.match(/hsla?\(\s*[\d.-]+\s*,?\s*([\d.]+)%/i);
+  if (hsl) return parseFloat(hsl[1]) < 10;
+
+  // hwb(hue whiteness% blackness%) — a pixel is fully gray when
+  // whiteness + blackness >= 100; chroma-like saturation = 1 - (w+b)/100.
+  const hwb = color.match(/hwb\(\s*[\d.-]+\s+([\d.]+)%\s+([\d.]+)%/i);
+  if (hwb) {
+    const w = parseFloat(hwb[1]), b = parseFloat(hwb[2]);
+    return (1 - Math.min(100, w + b) / 100) < 0.1;
+  }
+
+  // Unknown / unrecognized format — err on the side of DETECTING rather
+  // than silently skipping. This is the opposite of the previous default,
+  // which was the root cause of the oklch bug.
+  return false;
+}
+
+function parseRgb(color) {
+  if (!color || color === 'transparent') return null;
+  const m = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
+  if (!m) return null;
+  return { r: +m[1], g: +m[2], b: +m[3], a: m[4] !== undefined ? +m[4] : 1 };
+}
+
+function relativeLuminance({ r, g, b }) {
+  const [rs, gs, bs] = [r / 255, g / 255, b / 255].map(c =>
+    c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4
+  );
+  return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
+}
+
+function contrastRatio(c1, c2) {
+  const l1 = relativeLuminance(c1);
+  const l2 = relativeLuminance(c2);
+  return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
+}
+
+function parseGradientColors(bgImage) {
+  if (!bgImage || !bgImage.includes('gradient')) return [];
+  const colors = [];
+  for (const m of bgImage.matchAll(/rgba?\([^)]+\)/g)) {
+    const c = parseRgb(m[0]);
+    if (c) colors.push(c);
+  }
+  for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) {
+    const h = m[1];
+    if (h.length === 6) {
+      colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 });
+    } else {
+      colors.push({ r: parseInt(h[0]+h[0],16), g: parseInt(h[1]+h[1],16), b: parseInt(h[2]+h[2],16), a: 1 });
+    }
+  }
+  return colors;
+}
+
+function hasChroma(c, threshold = 30) {
+  if (!c) return false;
+  return (Math.max(c.r, c.g, c.b) - Math.min(c.r, c.g, c.b)) >= threshold;
+}
+
+function getHue(c) {
+  if (!c) return 0;
+  const r = c.r / 255, g = c.g / 255, b = c.b / 255;
+  const max = Math.max(r, g, b), min = Math.min(r, g, b);
+  if (max === min) return 0;
+  const d = max - min;
+  let h;
+  if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
+  else if (max === g) h = ((b - r) / d + 2) / 6;
+  else h = ((r - g) / d + 4) / 6;
+  return Math.round(h * 360);
+}
+
+function colorToHex(c) {
+  if (!c) return '?';
+  return '#' + [c.r, c.g, c.b].map(v => v.toString(16).padStart(2, '0')).join('');
+}
+
+// ─── Section 3: Pure Detection ──────────────────────────────────────────────
+
+function checkBorders(tag, widths, colors, radius) {
+  if (BORDER_SAFE_TAGS.has(tag)) return [];
+  const findings = [];
+  const sides = ['Top', 'Right', 'Bottom', 'Left'];
+
+  for (const side of sides) {
+    const w = widths[side];
+    if (w < 1 || isNeutralColor(colors[side])) continue;
+
+    const otherSides = sides.filter(s => s !== side);
+    const maxOther = Math.max(...otherSides.map(s => widths[s]));
+    if (!(w >= 2 && (maxOther <= 1 || w >= maxOther * 2))) continue;
+
+    const sn = side.toLowerCase();
+    const isSide = side === 'Left' || side === 'Right';
+
+    if (isSide) {
+      if (radius > 0) findings.push({ id: 'side-tab', snippet: `border-${sn}: ${w}px + border-radius: ${radius}px` });
+      else if (w >= 3) findings.push({ id: 'side-tab', snippet: `border-${sn}: ${w}px` });
+    } else {
+      if (radius > 0 && w >= 2) findings.push({ id: 'border-accent-on-rounded', snippet: `border-${sn}: ${w}px + border-radius: ${radius}px` });
+    }
+  }
+
+  return findings;
+}
+
+// Returns true if the given text is composed entirely of emoji characters
+// (plus whitespace / variation selectors). Emojis render as multicolor glyphs
+// regardless of CSS `color`, so contrast checks against the element's text
+// color are meaningless for these nodes.
+const EMOJI_CHAR_RE = /[\u{1F1E6}-\u{1F1FF}\u{1F300}-\u{1F9FF}\u{1FA00}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}\u{FE0F}\u{200D}\u{1F3FB}-\u{1F3FF}]/u;
+const EMOJI_CHARS_GLOBAL = /[\u{1F1E6}-\u{1F1FF}\u{1F300}-\u{1F9FF}\u{1FA00}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}\u{FE0F}\u{200D}\u{1F3FB}-\u{1F3FF}]/gu;
+function isEmojiOnlyText(text) {
+  if (!text) return false;
+  if (!EMOJI_CHAR_RE.test(text)) return false;
+  return text.replace(EMOJI_CHARS_GLOBAL, '').trim() === '';
+}
+
+function checkColors(opts) {
+  const { tag, textColor, bgColor, effectiveBg, effectiveBgStops, fontSize, fontWeight, hasDirectText, isEmojiOnly, bgClip, bgImage, classList } = opts;
+  if (SAFE_TAGS.has(tag)) return [];
+  const findings = [];
+
+  // Pure black background (only solid or near-solid, not semi-transparent overlays)
+  if (bgColor && bgColor.a >= 0.9 && bgColor.r === 0 && bgColor.g === 0 && bgColor.b === 0) {
+    findings.push({ id: 'pure-black-white', snippet: '#000000 background' });
+  }
+
+  if (hasDirectText && textColor && !isEmojiOnly) {
+    // Run background-dependent checks against either a solid bg or, if the
+    // ancestor is a gradient, against every gradient stop (use the worst case).
+    const bgs = effectiveBg ? [effectiveBg] : (effectiveBgStops && effectiveBgStops.length ? effectiveBgStops : null);
+    if (bgs) {
+      // Gray on colored background — flag if every stop is chromatic
+      const textLum = relativeLuminance(textColor);
+      const isGray = !hasChroma(textColor, 20) && textLum > 0.05 && textLum < 0.85;
+      if (isGray && bgs.every(b => hasChroma(b, 40))) {
+        const bgLabel = effectiveBg ? colorToHex(effectiveBg) : `gradient(${bgs.map(colorToHex).join(', ')})`;
+        findings.push({ id: 'gray-on-color', snippet: `text ${colorToHex(textColor)} on bg ${bgLabel}` });
+      }
+
+      // Low contrast (WCAG AA) — worst case across all bg stops
+      const ratios = bgs.map(b => contrastRatio(textColor, b));
+      let worstIdx = 0;
+      for (let i = 1; i < ratios.length; i++) if (ratios[i] < ratios[worstIdx]) worstIdx = i;
+      const ratio = ratios[worstIdx];
+      const isHeading = ['h1', 'h2', 'h3'].includes(tag);
+      const isLargeText = fontSize >= 18 || (fontSize >= 14 && fontWeight >= 700) || isHeading;
+      const threshold = isLargeText ? 3.0 : 4.5;
+      if (ratio < threshold) {
+        findings.push({ id: 'low-contrast', snippet: `${ratio.toFixed(1)}:1 (need ${threshold}:1) — text ${colorToHex(textColor)} on ${colorToHex(bgs[worstIdx])}` });
+      }
+    }
+
+    // AI palette: purple/violet on headings
+    if (hasChroma(textColor, 50)) {
+      const hue = getHue(textColor);
+      if (hue >= 260 && hue <= 310 && (['h1', 'h2', 'h3'].includes(tag) || fontSize >= 20)) {
+        findings.push({ id: 'ai-color-palette', snippet: `Purple/violet text (${colorToHex(textColor)}) on heading` });
+      }
+    }
+  }
+
+  // Gradient text
+  if (bgClip === 'text' && bgImage && bgImage.includes('gradient')) {
+    findings.push({ id: 'gradient-text', snippet: 'background-clip: text + gradient' });
+  }
+
+  // Tailwind class checks
+  if (classList) {
+    const classStr = typeof classList === 'string' ? classList : Array.from(classList).join(' ');
+    if (/\bbg-black\b(?!\/)/.test(classStr)) {
+      findings.push({ id: 'pure-black-white', snippet: 'bg-black' });
+    }
+
+    const grayMatch = classStr.match(/\btext-(?:gray|slate|zinc|neutral|stone)-\d+\b/);
+    const colorBgMatch = classStr.match(/\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/);
+    if (grayMatch && colorBgMatch) {
+      findings.push({ id: 'gray-on-color', snippet: `${grayMatch[0]} on ${colorBgMatch[0]}` });
+    }
+
+    if (/\bbg-clip-text\b/.test(classStr) && /\bbg-gradient-to-/.test(classStr)) {
+      findings.push({ id: 'gradient-text', snippet: 'bg-clip-text + bg-gradient (Tailwind)' });
+    }
+
+    const purpleText = classStr.match(/\btext-(?:purple|violet|indigo)-\d+\b/);
+    if (purpleText && (['h1', 'h2', 'h3'].includes(tag) || /\btext-(?:[2-9]xl)\b/.test(classStr))) {
+      findings.push({ id: 'ai-color-palette', snippet: `${purpleText[0]} on heading` });
+    }
+
+    if (/\bfrom-(?:purple|violet|indigo)-\d+\b/.test(classStr) && /\bto-(?:purple|violet|indigo|blue|cyan|pink|fuchsia)-\d+\b/.test(classStr)) {
+      findings.push({ id: 'ai-color-palette', snippet: 'Purple/violet gradient (Tailwind)' });
+    }
+  }
+
+  return findings;
+}
+
+function isCardLikeFromProps(hasShadow, hasBorder, hasRadius, hasBg) {
+  if (!hasShadow && !hasBorder) return false;
+  return hasRadius || hasBg;
+}
+
+const HEADING_TAGS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']);
+
+// Pure check: given a heading and metrics about its previousElementSibling,
+// decide if the sibling is the canonical "icon-tile-stacked-above-heading" shape.
+//
+// Triggers when ALL of the following hold for the sibling:
+//   • size 32–128px on both axes (not too small, not a hero image)
+//   • aspect ratio 0.7–1.4 (squarish — excludes wide thumbnails / pill badges)
+//   • has a non-transparent background-color, background-image, OR a visible border
+//     (covers solid colors, white-with-border, gradients — anything that visually
+//      defines a tile)
+//   • border-radius < width/2 (excludes round avatars; rounded squares pass)
+//   • contains an <svg> or icon-class <i> element that's smaller than the tile
+//   • the tile sits above the heading (its bottom is above the heading's top)
+function checkIconTile(opts) {
+  const { headingTag, headingText, headingTop,
+          siblingTag, siblingWidth, siblingHeight, siblingBottom,
+          siblingBgColor, siblingBgImage, siblingBorderWidth, siblingBorderRadius,
+          hasIconChild, iconChildWidth } = opts;
+  if (!HEADING_TAGS.has(headingTag)) return [];
+  if (!siblingTag) return [];
+  // Don't recurse into nested headings (e.g. h2 above h3 in a section header)
+  if (HEADING_TAGS.has(siblingTag)) return [];
+
+  // Size window: 32–128px on each axis
+  if (!(siblingWidth >= 32 && siblingWidth <= 128)) return [];
+  if (!(siblingHeight >= 32 && siblingHeight <= 128)) return [];
+
+  // Squarish aspect ratio
+  const ratio = siblingWidth / siblingHeight;
+  if (ratio < 0.7 || ratio > 1.4) return [];
+
+  // Must have something that visually defines the tile
+  const bgVisible = (siblingBgColor && siblingBgColor.a > 0.1)
+    || (siblingBgImage && siblingBgImage !== 'none' && siblingBgImage !== '');
+  const borderVisible = siblingBorderWidth > 0;
+  if (!bgVisible && !borderVisible) return [];
+
+  // Exclude circles (avatars). Rounded squares pass.
+  if (siblingBorderRadius >= siblingWidth / 2) return [];
+
+  // Must contain an icon element smaller than the tile
+  if (!hasIconChild) return [];
+  if (iconChildWidth && iconChildWidth >= siblingWidth * 0.95) return [];
+
+  // Vertical stacking: tile must end above where the heading starts.
+  // (Allow the check to skip when both top/bottom are 0 — jsdom layout case.)
+  if (headingTop && siblingBottom && siblingBottom > headingTop + 4) return [];
+
+  const text = (headingText || '').trim().slice(0, 60);
+  return [{
+    id: 'icon-tile-stack',
+    snippet: `${Math.round(siblingWidth)}x${Math.round(siblingHeight)}px icon tile above ${headingTag} "${text}"`,
+  }];
+}
+
+const LAYOUT_TRANSITION_PROPS = new Set([
+  'width', 'height', 'padding', 'margin',
+  'max-height', 'max-width', 'min-height', 'min-width',
+  'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
+  'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
+]);
+
+function checkMotion(opts) {
+  const { tag, transitionProperty, animationName, timingFunctions, classList } = opts;
+  if (SAFE_TAGS.has(tag)) return [];
+  const findings = [];
+
+  // --- Bounce/elastic easing ---
+  if (animationName && animationName !== 'none' && /bounce|elastic|wobble|jiggle|spring/i.test(animationName)) {
+    findings.push({ id: 'bounce-easing', snippet: `animation: ${animationName}` });
+  }
+  if (classList && /\banimate-bounce\b/.test(classList)) {
+    findings.push({ id: 'bounce-easing', snippet: 'animate-bounce (Tailwind)' });
+  }
+
+  // Check timing functions for overshoot cubic-bezier (y values outside [0, 1])
+  if (timingFunctions) {
+    const bezierRe = /cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)/g;
+    let m;
+    while ((m = bezierRe.exec(timingFunctions)) !== null) {
+      const y1 = parseFloat(m[2]), y2 = parseFloat(m[4]);
+      if (y1 < -0.1 || y1 > 1.1 || y2 < -0.1 || y2 > 1.1) {
+        findings.push({ id: 'bounce-easing', snippet: `cubic-bezier(${m[1]}, ${m[2]}, ${m[3]}, ${m[4]})` });
+        break;
+      }
+    }
+  }
+
+  // --- Layout property transition ---
+  if (transitionProperty && transitionProperty !== 'all' && transitionProperty !== 'none') {
+    const props = transitionProperty.split(',').map(p => p.trim().toLowerCase());
+    const layoutFound = props.filter(p => LAYOUT_TRANSITION_PROPS.has(p));
+    if (layoutFound.length > 0) {
+      findings.push({ id: 'layout-transition', snippet: `transition: ${layoutFound.join(', ')}` });
+    }
+  }
+
+  return findings;
+}
+
+function checkGlow(opts) {
+  const { boxShadow, effectiveBg } = opts;
+  if (!boxShadow || boxShadow === 'none') return [];
+  if (!effectiveBg) return [];
+
+  // Only flag on dark backgrounds (luminance < 0.1)
+  const bgLum = relativeLuminance(effectiveBg);
+  if (bgLum >= 0.1) return [];
+
+  // Split multiple shadows (commas not inside parentheses)
+  const parts = boxShadow.split(/,(?![^(]*\))/);
+  for (const shadow of parts) {
+    const colorMatch = shadow.match(/rgba?\([^)]+\)/);
+    if (!colorMatch) continue;
+    const color = parseRgb(colorMatch[0]);
+    if (!color || !hasChroma(color, 30)) continue;
+
+    // Extract px values — in computed style: "color Xpx Ypx BLURpx [SPREADpx]"
+    const afterColor = shadow.substring(shadow.indexOf(colorMatch[0]) + colorMatch[0].length);
+    const beforeColor = shadow.substring(0, shadow.indexOf(colorMatch[0]));
+    const pxVals = [...beforeColor.matchAll(/([\d.]+)px/g), ...afterColor.matchAll(/([\d.]+)px/g)]
+      .map(m => parseFloat(m[1]));
+
+    // Third value is blur (offset-x, offset-y, blur, [spread])
+    if (pxVals.length >= 3 && pxVals[2] > 4) {
+      return [{ id: 'dark-glow', snippet: `Colored glow (${colorToHex(color)}) on dark background` }];
+    }
+  }
+
+  return [];
+}
+
+/**
+ * Regex-on-HTML checks shared between browser and Node page-level detection.
+ * These don't need DOM access, just the raw HTML string.
+ */
+function checkHtmlPatterns(html) {
+  const findings = [];
+
+  // --- Color ---
+
+  // Pure black background
+  const pureBlackBgRe = /background(?:-color)?\s*:\s*(?:#000000|#000|rgb\(\s*0,\s*0,\s*0\s*\))\b/gi;
+  if (pureBlackBgRe.test(html)) {
+    findings.push({ id: 'pure-black-white', snippet: 'Pure #000 background' });
+  }
+
+  // AI color palette: purple/violet
+  const purpleHexRe = /#(?:7c3aed|8b5cf6|a855f7|9333ea|7e22ce|6d28d9|6366f1|764ba2|667eea)\b/gi;
+  if (purpleHexRe.test(html)) {
+    const purpleTextRe = /(?:(?:^|;)\s*color\s*:\s*(?:.*?)(?:#(?:7c3aed|8b5cf6|a855f7|9333ea|7e22ce|6d28d9))|gradient.*?#(?:7c3aed|8b5cf6|a855f7|764ba2|667eea))/gi;
+    if (purpleTextRe.test(html)) {
+      findings.push({ id: 'ai-color-palette', snippet: 'Purple/violet accent colors detected' });
+    }
+  }
+
+  // Gradient text (background-clip: text + gradient)
+  const gradientRe = /(?:-webkit-)?background-clip\s*:\s*text/gi;
+  let gm;
+  while ((gm = gradientRe.exec(html)) !== null) {
+    const start = Math.max(0, gm.index - 200);
+    const context = html.substring(start, gm.index + gm[0].length + 200);
+    if (/gradient/i.test(context)) {
+      findings.push({ id: 'gradient-text', snippet: 'background-clip: text + gradient' });
+      break;
+    }
+  }
+  if (/\bbg-clip-text\b/.test(html) && /\bbg-gradient-to-/.test(html)) {
+    findings.push({ id: 'gradient-text', snippet: 'bg-clip-text + bg-gradient (Tailwind)' });
+  }
+
+  // --- Layout ---
+
+  // Monotonous spacing
+  const spacingValues = [];
+  const spacingRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*(\d+)px/gi;
+  let sm;
+  while ((sm = spacingRe.exec(html)) !== null) {
+    const v = parseInt(sm[1], 10);
+    if (v > 0 && v < 200) spacingValues.push(v);
+  }
+  const gapRe = /gap\s*:\s*(\d+)px/gi;
+  while ((sm = gapRe.exec(html)) !== null) {
+    spacingValues.push(parseInt(sm[1], 10));
+  }
+  const twSpaceRe = /\b(?:p|px|py|pt|pb|pl|pr|m|mx|my|mt|mb|ml|mr|gap)-(\d+)\b/g;
+  while ((sm = twSpaceRe.exec(html)) !== null) {
+    spacingValues.push(parseInt(sm[1], 10) * 4);
+  }
+  const remSpacingRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*([\d.]+)rem/gi;
+  while ((sm = remSpacingRe.exec(html)) !== null) {
+    const v = Math.round(parseFloat(sm[1]) * 16);
+    if (v > 0 && v < 200) spacingValues.push(v);
+  }
+  const roundedSpacing = spacingValues.map(v => Math.round(v / 4) * 4);
+  if (roundedSpacing.length >= 10) {
+    const counts = {};
+    for (const v of roundedSpacing) counts[v] = (counts[v] || 0) + 1;
+    const maxCount = Math.max(...Object.values(counts));
+    const dominantPct = maxCount / roundedSpacing.length;
+    const unique = [...new Set(roundedSpacing)].filter(v => v > 0);
+    if (dominantPct > 0.6 && unique.length <= 3) {
+      const dominant = Object.entries(counts).sort((a, b) => b[1] - a[1])[0][0];
+      findings.push({
+        id: 'monotonous-spacing',
+        snippet: `~${dominant}px used ${maxCount}/${roundedSpacing.length} times (${Math.round(dominantPct * 100)}%)`,
+      });
+    }
+  }
+
+  // --- Motion ---
+
+  // Bounce/elastic animation names
+  const bounceRe = /animation(?:-name)?\s*:\s*[^;]*\b(bounce|elastic|wobble|jiggle|spring)\b/gi;
+  if (bounceRe.test(html)) {
+    findings.push({ id: 'bounce-easing', snippet: 'Bounce/elastic animation in CSS' });
+  }
+
+  // Overshoot cubic-bezier
+  const bezierRe = /cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)/g;
+  let bm;
+  while ((bm = bezierRe.exec(html)) !== null) {
+    const y1 = parseFloat(bm[2]), y2 = parseFloat(bm[4]);
+    if (y1 < -0.1 || y1 > 1.1 || y2 < -0.1 || y2 > 1.1) {
+      findings.push({ id: 'bounce-easing', snippet: `cubic-bezier(${bm[1]}, ${bm[2]}, ${bm[3]}, ${bm[4]})` });
+      break;
+    }
+  }
+
+  // Layout property transitions
+  const transRe = /transition(?:-property)?\s*:\s*([^;{}]+)/gi;
+  let tm;
+  while ((tm = transRe.exec(html)) !== null) {
+    const val = tm[1].toLowerCase();
+    if (/\ball\b/.test(val)) continue;
+    const found = val.match(/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi);
+    if (found) {
+      findings.push({ id: 'layout-transition', snippet: `transition: ${found.join(', ')}` });
+      break;
+    }
+  }
+
+  // --- Dark glow ---
+
+  const darkBgRe = /background(?:-color)?\s*:\s*(?:#(?:0[0-9a-f]|1[0-9a-f]|2[0-3])[0-9a-f]{4}\b|#(?:0|1)[0-9a-f]{2}\b|rgb\(\s*(\d{1,2})\s*,\s*(\d{1,2})\s*,\s*(\d{1,2})\s*\))/gi;
+  const twDarkBg = /\bbg-(?:gray|slate|zinc|neutral|stone)-(?:9\d{2}|800)\b/;
+  if (darkBgRe.test(html) || twDarkBg.test(html)) {
+    const shadowRe = /box-shadow\s*:\s*([^;{}]+)/gi;
+    let shm;
+    while ((shm = shadowRe.exec(html)) !== null) {
+      const val = shm[1];
+      const colorMatch = val.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
+      if (!colorMatch) continue;
+      const [r, g, b] = [+colorMatch[1], +colorMatch[2], +colorMatch[3]];
+      if ((Math.max(r, g, b) - Math.min(r, g, b)) < 30) continue;
+      const pxVals = [...val.matchAll(/(\d+)px|(?<![.\d])\b(0)\b(?![.\d])/g)].map(p => +(p[1] || p[2]));
+      if (pxVals.length >= 3 && pxVals[2] > 4) {
+        findings.push({ id: 'dark-glow', snippet: `Colored glow (rgb(${r},${g},${b})) on dark page` });
+        break;
+      }
+    }
+  }
+
+  return findings;
+}
+
+// ─── Section 4: resolveBackground (unified) ─────────────────────────────────
+
+function resolveBackground(el, win) {
+  let current = el;
+  while (current && current.nodeType === 1) {
+    const style = IS_BROWSER ? getComputedStyle(current) : win.getComputedStyle(current);
+
+    // If this element has a background-image (gradient or url), it's visually
+    // opaque but we can't determine the effective color — bail out so callers
+    // don't get a false solid-color answer.
+    const bgImage = style.backgroundImage || '';
+    if (bgImage && bgImage !== 'none' && (/gradient/i.test(bgImage) || /url\s*\(/i.test(bgImage))) {
+      return null;
+    }
+
+    let bg = parseRgb(style.backgroundColor);
+    if (!IS_BROWSER && (!bg || bg.a < 0.1)) {
+      // jsdom doesn't decompose background shorthand — parse raw style attr
+      const rawStyle = current.getAttribute?.('style') || '';
+      const bgMatch = rawStyle.match(/background(?:-color)?\s*:\s*([^;]+)/i);
+      const inlineBg = bgMatch ? bgMatch[1].trim() : '';
+      // Check for gradient or url() image in inline style too
+      if (/gradient/i.test(inlineBg) || /url\s*\(/i.test(inlineBg)) return null;
+      bg = parseRgb(inlineBg);
+      if (!bg && inlineBg) {
+        const hexMatch = inlineBg.match(/#([0-9a-f]{6}|[0-9a-f]{3})\b/i);
+        if (hexMatch) {
+          const h = hexMatch[1];
+          if (h.length === 6) {
+            bg = { r: parseInt(h.slice(0,2), 16), g: parseInt(h.slice(2,4), 16), b: parseInt(h.slice(4,6), 16), a: 1 };
+          } else {
+            bg = { r: parseInt(h[0]+h[0], 16), g: parseInt(h[1]+h[1], 16), b: parseInt(h[2]+h[2], 16), a: 1 };
+          }
+        }
+      }
+    }
+    if (bg && bg.a > 0.1) {
+      if (IS_BROWSER || bg.a >= 0.5) return bg;
+    }
+    current = current.parentElement;
+  }
+  return { r: 255, g: 255, b: 255 };
+}
+
+// Walk parents looking for a gradient background and return its color stops.
+// Used as a fallback when resolveBackground() returns null because the
+// effective background is a gradient (no single solid color to compare against).
+function resolveGradientStops(el, win) {
+  let current = el;
+  while (current && current.nodeType === 1) {
+    const style = IS_BROWSER ? getComputedStyle(current) : win.getComputedStyle(current);
+    const bgImage = style.backgroundImage || '';
+    if (bgImage && bgImage !== 'none' && /gradient/i.test(bgImage)) {
+      const stops = parseGradientColors(bgImage);
+      if (stops.length > 0) return stops;
+    }
+    if (!IS_BROWSER) {
+      // jsdom doesn't decompose `background:` shorthand — peek at the raw inline style
+      const rawStyle = current.getAttribute?.('style') || '';
+      const bgMatch = rawStyle.match(/background(?:-image)?\s*:\s*([^;]+)/i);
+      if (bgMatch && /gradient/i.test(bgMatch[1])) {
+        const stops = parseGradientColors(bgMatch[1]);
+        if (stops.length > 0) return stops;
+      }
+    }
+    current = current.parentElement;
+  }
+  return null;
+}
+
+// ─── Section 5: Element Adapters ────────────────────────────────────────────
+
+// Browser adapters — call getComputedStyle/getBoundingClientRect on live DOM
+
+function checkElementBordersDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (BORDER_SAFE_TAGS.has(tag)) return [];
+  const rect = el.getBoundingClientRect();
+  if (rect.width < 20 || rect.height < 20) return [];
+  const style = getComputedStyle(el);
+  const sides = ['Top', 'Right', 'Bottom', 'Left'];
+  const widths = {}, colors = {};
+  for (const s of sides) {
+    widths[s] = parseFloat(style[`border${s}Width`]) || 0;
+    colors[s] = style[`border${s}Color`] || '';
+  }
+  return checkBorders(tag, widths, colors, parseFloat(style.borderRadius) || 0);
+}
+
+function checkElementColorsDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (SAFE_TAGS.has(tag)) return [];
+  const rect = el.getBoundingClientRect();
+  if (rect.width < 10 || rect.height < 10) return [];
+  const style = getComputedStyle(el);
+  const directText = [...el.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
+  const hasDirectText = directText.trim().length > 0;
+  const effectiveBg = resolveBackground(el);
+  return checkColors({
+    tag,
+    textColor: parseRgb(style.color),
+    bgColor: parseRgb(style.backgroundColor),
+    effectiveBg,
+    effectiveBgStops: effectiveBg ? null : resolveGradientStops(el),
+    fontSize: parseFloat(style.fontSize) || 16,
+    fontWeight: parseInt(style.fontWeight) || 400,
+    hasDirectText,
+    isEmojiOnly: isEmojiOnlyText(directText),
+    bgClip: style.webkitBackgroundClip || style.backgroundClip || '',
+    bgImage: style.backgroundImage || '',
+    classList: el.getAttribute('class') || '',
+  });
+}
+
+function checkElementIconTileDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (!HEADING_TAGS.has(tag)) return [];
+  const sibling = el.previousElementSibling;
+  if (!sibling) return [];
+
+  const sibRect = sibling.getBoundingClientRect();
+  const headRect = el.getBoundingClientRect();
+  const sibStyle = getComputedStyle(sibling);
+
+  // The tile may either contain an <svg>/<i> icon child, OR the tile itself
+  // may contain an emoji/symbol character directly as its only text content
+  // (the "card-icon" pattern from many AI-generated demos).
+  const iconChild = sibling.querySelector('svg, i[data-lucide], i[class*="fa-"], i[class*="icon"]');
+  const iconRect = iconChild?.getBoundingClientRect();
+  const sibDirectText = [...sibling.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
+  const hasInlineEmojiIcon = sibling.children.length === 0 && isEmojiOnlyText(sibDirectText);
+
+  return checkIconTile({
+    headingTag: tag,
+    headingText: el.textContent || '',
+    headingTop: headRect.top,
+    siblingTag: sibling.tagName.toLowerCase(),
+    siblingWidth: sibRect.width,
+    siblingHeight: sibRect.height,
+    siblingBottom: sibRect.bottom,
+    siblingBgColor: parseRgb(sibStyle.backgroundColor),
+    siblingBgImage: sibStyle.backgroundImage || '',
+    siblingBorderWidth: parseFloat(sibStyle.borderTopWidth) || 0,
+    siblingBorderRadius: parseFloat(sibStyle.borderRadius) || 0,
+    hasIconChild: !!iconChild || hasInlineEmojiIcon,
+    iconChildWidth: iconRect?.width || 0,
+  });
+}
+
+function checkElementMotionDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (SAFE_TAGS.has(tag)) return [];
+  const style = getComputedStyle(el);
+  return checkMotion({
+    tag,
+    transitionProperty: style.transitionProperty || '',
+    animationName: style.animationName || '',
+    timingFunctions: [style.animationTimingFunction, style.transitionTimingFunction].filter(Boolean).join(' '),
+    classList: el.getAttribute('class') || '',
+  });
+}
+
+function checkElementGlowDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  const style = getComputedStyle(el);
+  if (!style.boxShadow || style.boxShadow === 'none') return [];
+  // Use parent's background — glow radiates outward, so the surrounding context matters
+  // If resolveBackground returns null (gradient), try to infer from the gradient colors
+  let parentBg = el.parentElement ? resolveBackground(el.parentElement) : resolveBackground(el);
+  if (!parentBg) {
+    // Gradient background — sample its colors to determine if it's dark
+    let cur = el.parentElement;
+    while (cur && cur.nodeType === 1) {
+      const bgImage = getComputedStyle(cur).backgroundImage || '';
+      const gradColors = parseGradientColors(bgImage);
+      if (gradColors.length > 0) {
+        // Average the gradient colors
+        const avg = { r: 0, g: 0, b: 0 };
+        for (const c of gradColors) { avg.r += c.r; avg.g += c.g; avg.b += c.b; }
+        avg.r = Math.round(avg.r / gradColors.length);
+        avg.g = Math.round(avg.g / gradColors.length);
+        avg.b = Math.round(avg.b / gradColors.length);
+        parentBg = avg;
+        break;
+      }
+      cur = cur.parentElement;
+    }
+  }
+  return checkGlow({ tag, boxShadow: style.boxShadow, effectiveBg: parentBg });
+}
+
+function checkElementAIPaletteDOM(el) {
+  const style = getComputedStyle(el);
+  const findings = [];
+
+  // Check gradient backgrounds for purple/violet or cyan
+  const bgImage = style.backgroundImage || '';
+  const gradColors = parseGradientColors(bgImage);
+  for (const c of gradColors) {
+    if (hasChroma(c, 50)) {
+      const hue = getHue(c);
+      if (hue >= 260 && hue <= 310) {
+        findings.push({ id: 'ai-color-palette', snippet: 'Purple/violet gradient background' });
+        break;
+      }
+      if (hue >= 160 && hue <= 200) {
+        findings.push({ id: 'ai-color-palette', snippet: 'Cyan gradient background' });
+        break;
+      }
+    }
+  }
+
+  // Check for neon text (vivid cyan/purple color on dark background)
+  const textColor = parseRgb(style.color);
+  if (textColor && hasChroma(textColor, 80)) {
+    const hue = getHue(textColor);
+    const isAIPalette = (hue >= 160 && hue <= 200) || (hue >= 260 && hue <= 310);
+    if (isAIPalette) {
+      const parentBg = el.parentElement ? resolveBackground(el.parentElement) : null;
+      // Also check gradient parents
+      let effectiveBg = parentBg;
+      if (!effectiveBg) {
+        let cur = el.parentElement;
+        while (cur && cur.nodeType === 1) {
+          const gi = getComputedStyle(cur).backgroundImage || '';
+          const gc = parseGradientColors(gi);
+          if (gc.length > 0) {
+            const avg = { r: 0, g: 0, b: 0 };
+            for (const c of gc) { avg.r += c.r; avg.g += c.g; avg.b += c.b; }
+            avg.r = Math.round(avg.r / gc.length);
+            avg.g = Math.round(avg.g / gc.length);
+            avg.b = Math.round(avg.b / gc.length);
+            effectiveBg = avg;
+            break;
+          }
+          cur = cur.parentElement;
+        }
+      }
+      if (effectiveBg && relativeLuminance(effectiveBg) < 0.1) {
+        const label = hue >= 260 ? 'Purple/violet' : 'Cyan';
+        findings.push({ id: 'ai-color-palette', snippet: `${label} neon text on dark background` });
+      }
+    }
+  }
+
+  return findings;
+}
+
+const QUALITY_TEXT_TAGS = new Set(['p', 'li', 'td', 'th', 'dd', 'blockquote', 'figcaption']);
+
+// Resolve a CSS font-size value to pixels by walking up the parent chain.
+// Browsers resolve em/rem/% to px in getComputedStyle, but jsdom returns the
+// specified value verbatim — so for the Node path we walk parents ourselves.
+function resolveFontSizePx(el, win) {
+  const chain = []; // raw font-size strings, leaf → root
+  let cur = el;
+  while (cur && cur.nodeType === 1) {
+    const fs = (win ? win.getComputedStyle(cur) : getComputedStyle(cur)).fontSize;
+    chain.push(fs || '');
+    cur = cur.parentElement;
+  }
+  // Walk root → leaf, resolving each value relative to its parent context.
+  let px = 16; // root default
+  for (let i = chain.length - 1; i >= 0; i--) {
+    const v = chain[i];
+    if (!v || v === 'inherit') continue;
+    const num = parseFloat(v);
+    if (isNaN(num)) continue;
+    if (v.endsWith('px')) px = num;
+    else if (v.endsWith('rem')) px = num * 16;
+    else if (v.endsWith('em')) px = num * px;
+    else if (v.endsWith('%')) px = (num / 100) * px;
+    else px = num; // unitless — already resolved
+  }
+  return px;
+}
+
+// Resolve a CSS length value (line-height, letter-spacing, etc.) given a
+// known font-size context. Returns null for "normal" / unparseable values.
+function resolveLengthPx(value, fontSizePx) {
+  if (!value || value === 'normal' || value === 'auto' || value === 'inherit') return null;
+  const num = parseFloat(value);
+  if (isNaN(num)) return null;
+  if (value.endsWith('px')) return num;
+  if (value.endsWith('rem')) return num * 16;
+  if (value.endsWith('em')) return num * fontSizePx;
+  if (value.endsWith('%')) return (num / 100) * fontSizePx;
+  // Unitless line-height = multiplier, return px equivalent
+  return num * fontSizePx;
+}
+
+// Pure quality checks. Most run on computed CSS and DOM-only inputs (work in
+// jsdom and the browser). Two checks (line-length, cramped-padding) gate on
+// element rect dimensions, which jsdom can't compute — pass `rect: null` from
+// the Node adapter to skip those.
+//
+// Both adapters resolve font-size, line-height and letter-spacing to pixels
+// before calling this so the pure function only deals with numbers.
+function checkQuality(opts) {
+  const { el, tag, style, hasDirectText, textLen, fontSize, lineHeightPx, letterSpacingPx, rect, lineMax = 80 } = opts;
+  const findings = [];
+  // Skip browser extension injected elements
+  const elId = el.id || '';
+  if (elId.startsWith('claude-') || elId.startsWith('cic-')) return findings;
+
+  // --- Line length too long --- (browser-only: needs rect.width)
+  if (rect && hasDirectText && QUALITY_TEXT_TAGS.has(tag) && rect.width > 0 && textLen > lineMax) {
+    const charsPerLine = rect.width / (fontSize * 0.5);
+    if (charsPerLine > lineMax + 5) {
+      findings.push({ id: 'line-length', snippet: `~${Math.round(charsPerLine)} chars/line (aim for <${lineMax})` });
+    }
+  }
+
+  // --- Cramped padding --- (browser-only: needs rect to skip small badges/labels)
+  // Vertical and horizontal thresholds are independent because line-height
+  // already provides built-in vertical breathing room (the line box is taller
+  // than the cap height), but horizontal has no equivalent. Both scale with
+  // font-size — bigger text demands proportionally more padding.
+  //   vertical:   max(4px, fontSize × 0.3)
+  //   horizontal: max(8px, fontSize × 0.5)
+  if (rect && hasDirectText && textLen > 20 && rect.width > 100 && rect.height > 30) {
+    const borders = {
+      top: parseFloat(style.borderTopWidth) || 0,
+      right: parseFloat(style.borderRightWidth) || 0,
+      bottom: parseFloat(style.borderBottomWidth) || 0,
+      left: parseFloat(style.borderLeftWidth) || 0,
+    };
+    const borderCount = Object.values(borders).filter(w => w > 0).length;
+    const hasBg = style.backgroundColor && style.backgroundColor !== 'rgba(0, 0, 0, 0)';
+    if (borderCount >= 2 || hasBg) {
+      const vPads = [], hPads = [];
+      if (hasBg || borders.top > 0) vPads.push(parseFloat(style.paddingTop) || 0);
+      if (hasBg || borders.bottom > 0) vPads.push(parseFloat(style.paddingBottom) || 0);
+      if (hasBg || borders.left > 0) hPads.push(parseFloat(style.paddingLeft) || 0);
+      if (hasBg || borders.right > 0) hPads.push(parseFloat(style.paddingRight) || 0);
+
+      const vMin = vPads.length ? Math.min(...vPads) : Infinity;
+      const hMin = hPads.length ? Math.min(...hPads) : Infinity;
+      const vThresh = Math.max(4, fontSize * 0.3);
+      const hThresh = Math.max(8, fontSize * 0.5);
+
+      // Emit at most one finding per element — pick whichever axis is worse.
+      if (vMin < vThresh) {
+        findings.push({ id: 'cramped-padding', snippet: `${vMin}px vertical padding (need ≥${vThresh.toFixed(1)}px for ${fontSize}px text)` });
+      } else if (hMin < hThresh) {
+        findings.push({ id: 'cramped-padding', snippet: `${hMin}px horizontal padding (need ≥${hThresh.toFixed(1)}px for ${fontSize}px text)` });
+      }
+    }
+  }
+
+  // --- Tight line height ---
+  if (hasDirectText && textLen > 50 && !['h1','h2','h3','h4','h5','h6'].includes(tag)) {
+    if (lineHeightPx != null && fontSize > 0) {
+      const ratio = lineHeightPx / fontSize;
+      if (ratio > 0 && ratio < 1.3) {
+        findings.push({ id: 'tight-leading', snippet: `line-height ${ratio.toFixed(2)}x (need >=1.3)` });
+      }
+    }
+  }
+
+  // --- Justified text (without hyphens) ---
+  if (hasDirectText && style.textAlign === 'justify') {
+    const hyphens = style.hyphens || style.webkitHyphens || '';
+    if (hyphens !== 'auto') {
+      findings.push({ id: 'justified-text', snippet: 'text-align: justify without hyphens: auto' });
+    }
+  }
+
+  // --- Tiny body text ---
+  // Only flag actual body content, not UI labels (buttons, tabs, badges, captions, footer text, etc.)
+  if (hasDirectText && textLen > 20 && fontSize < 12) {
+    const skipTags = ['sub', 'sup', 'code', 'kbd', 'samp', 'var', 'caption', 'figcaption'];
+    const inUIContext = el.closest && el.closest('button, a, label, summary, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="option"], nav, footer, [class*="badge" i], [class*="chip" i], [class*="pill" i], [class*="tag" i], [class*="label" i], [class*="caption" i]');
+    const isUppercase = style.textTransform === 'uppercase';
+    if (!skipTags.includes(tag) && !inUIContext && !isUppercase) {
+      findings.push({ id: 'tiny-text', snippet: `${fontSize}px body text` });
+    }
+  }
+
+  // --- All-caps body text ---
+  if (hasDirectText && textLen > 30 && style.textTransform === 'uppercase') {
+    if (!['h1','h2','h3','h4','h5','h6'].includes(tag)) {
+      findings.push({ id: 'all-caps-body', snippet: `text-transform: uppercase on ${textLen} chars of body text` });
+    }
+  }
+
+  // --- Wide letter spacing on body text ---
+  if (hasDirectText && textLen > 20 && style.textTransform !== 'uppercase') {
+    if (letterSpacingPx != null && letterSpacingPx > 0 && fontSize > 0) {
+      const trackingEm = letterSpacingPx / fontSize;
+      if (trackingEm > 0.05) {
+        findings.push({ id: 'wide-tracking', snippet: `letter-spacing: ${trackingEm.toFixed(2)}em on body text` });
+      }
+    }
+  }
+
+  return findings;
+}
+
+function checkElementQualityDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  const style = getComputedStyle(el);
+  const hasDirectText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim().length > 10);
+  const textLen = el.textContent?.trim().length || 0;
+  // Browser getComputedStyle resolves everything to px — direct parseFloat
+  // works.
+  const fontSize = parseFloat(style.fontSize) || 16;
+  const lineHeightPx = resolveLengthPx(style.lineHeight, fontSize);
+  const letterSpacingPx = resolveLengthPx(style.letterSpacing, fontSize);
+  const rect = el.getBoundingClientRect();
+  const lineMax = (typeof window !== 'undefined' && window.__IMPECCABLE_CONFIG__?.lineLengthMax) || 80;
+  return checkQuality({ el, tag, style, hasDirectText, textLen, fontSize, lineHeightPx, letterSpacingPx, rect, lineMax });
+}
+
+// Pure page-level skipped-heading walk. Takes a Document so it works in both
+// the browser and jsdom.
+function checkPageQualityFromDoc(doc) {
+  const findings = [];
+  const headings = doc.querySelectorAll('h1, h2, h3, h4, h5, h6');
+  let prevLevel = 0;
+  let prevText = '';
+  for (const h of headings) {
+    const level = parseInt(h.tagName[1]);
+    const text = (h.textContent || '').trim().replace(/\s+/g, ' ').slice(0, 60);
+    if (prevLevel > 0 && level > prevLevel + 1) {
+      findings.push({
+        id: 'skipped-heading',
+        snippet: `<h${prevLevel}> "${prevText}" followed by <h${level}> "${text}" (missing h${prevLevel + 1})`,
+      });
+    }
+    prevLevel = level;
+    prevText = text;
+  }
+  return findings;
+}
+
+// Browser adapter (returns the legacy { type, detail } shape used by the overlay loop)
+function checkPageQualityDOM() {
+  return checkPageQualityFromDoc(document).map(f => ({ type: f.id, detail: f.snippet }));
+}
+
+// Node adapters — take pre-extracted jsdom computed style
+
+// jsdom doesn't lay out OR resolve em/rem/% to px — so we pre-resolve every
+// CSS length the rule needs ourselves (walking the parent chain for
+// font-size inheritance), and pass `rect: null` to skip the two rules that
+// genuinely need element rects (line-length, cramped-padding).
+function checkElementQuality(el, style, tag, window) {
+  const hasDirectText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim().length > 10);
+  const textLen = el.textContent?.trim().length || 0;
+  const fontSize = resolveFontSizePx(el, window);
+  const lineHeightPx = resolveLengthPx(style.lineHeight, fontSize);
+  const letterSpacingPx = resolveLengthPx(style.letterSpacing, fontSize);
+  return checkQuality({ el, tag, style, hasDirectText, textLen, fontSize, lineHeightPx, letterSpacingPx, rect: null });
+}
+
+function checkElementBorders(tag, style, overrides) {
+  const sides = ['Top', 'Right', 'Bottom', 'Left'];
+  const widths = {}, colors = {};
+  for (const s of sides) {
+    widths[s] = parseFloat(style[`border${s}Width`]) || 0;
+    colors[s] = style[`border${s}Color`] || '';
+    // jsdom silently drops any border shorthand containing var(), leaving
+    // both width and color empty on the computed style. When the detectHtml
+    // pre-pass pulled a resolved value off the rule, use it to fill in the
+    // missing side so the side-tab check can run. Real browsers resolve
+    // var() natively, so this fallback is a no-op in the browser path.
+    if (widths[s] === 0 && overrides && overrides[s]) {
+      widths[s] = overrides[s].width;
+      colors[s] = overrides[s].color;
+    } else if (colors[s] && colors[s].startsWith('var(') && overrides && overrides[s]) {
+      // Longhand case: jsdom kept the width but left the color as the
+      // literal `var(...)` string. Substitute the resolved color.
+      colors[s] = overrides[s].color;
+    }
+  }
+  return checkBorders(tag, widths, colors, parseFloat(style.borderRadius) || 0);
+}
+
+function checkElementColors(el, style, tag, window) {
+  const directText = [...el.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
+  const hasDirectText = directText.trim().length > 0;
+
+  const effectiveBg = resolveBackground(el, window);
+  return checkColors({
+    tag,
+    textColor: parseRgb(style.color),
+    bgColor: parseRgb(style.backgroundColor),
+    effectiveBg,
+    effectiveBgStops: effectiveBg ? null : resolveGradientStops(el, window),
+    fontSize: parseFloat(style.fontSize) || 16,
+    fontWeight: parseInt(style.fontWeight) || 400,
+    hasDirectText,
+    isEmojiOnly: isEmojiOnlyText(directText),
+    bgClip: style.webkitBackgroundClip || style.backgroundClip || '',
+    bgImage: style.backgroundImage || '',
+    classList: el.getAttribute?.('class') || el.className || '',
+  });
+}
+
+function checkElementIconTile(el, tag, window) {
+  if (!HEADING_TAGS.has(tag)) return [];
+  const sibling = el.previousElementSibling;
+  if (!sibling) return [];
+
+  const sibStyle = window.getComputedStyle(sibling);
+  // jsdom doesn't lay out — read explicit pixel dimensions from CSS instead.
+  const sibWidth = parseFloat(sibStyle.width) || 0;
+  const sibHeight = parseFloat(sibStyle.height) || 0;
+
+  const iconChild = sibling.querySelector('svg, i[data-lucide], i[class*="fa-"], i[class*="icon"]');
+  let iconWidth = 0;
+  if (iconChild) {
+    const iconStyle = window.getComputedStyle(iconChild);
+    iconWidth = parseFloat(iconStyle.width) || parseFloat(iconChild.getAttribute('width')) || 0;
+  }
+  // Or: tile contains an emoji/symbol character directly as its only content
+  const sibDirectText = [...sibling.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
+  const hasInlineEmojiIcon = sibling.children.length === 0 && isEmojiOnlyText(sibDirectText);
+
+  return checkIconTile({
+    headingTag: tag,
+    headingText: el.textContent || '',
+    headingTop: 0, // jsdom: no layout, skip vertical-stacking gate
+    siblingTag: sibling.tagName.toLowerCase(),
+    siblingWidth: sibWidth,
+    siblingHeight: sibHeight,
+    siblingBottom: 0,
+    siblingBgColor: parseRgb(sibStyle.backgroundColor),
+    siblingBgImage: sibStyle.backgroundImage || '',
+    siblingBorderWidth: parseFloat(sibStyle.borderTopWidth) || 0,
+    siblingBorderRadius: parseFloat(sibStyle.borderRadius) || 0,
+    hasIconChild: !!iconChild || hasInlineEmojiIcon,
+    iconChildWidth: iconWidth,
+  });
+}
+
+function checkElementMotion(tag, style) {
+  return checkMotion({
+    tag,
+    transitionProperty: style.transitionProperty || '',
+    animationName: style.animationName || '',
+    timingFunctions: [style.animationTimingFunction, style.transitionTimingFunction].filter(Boolean).join(' '),
+    classList: '',
+  });
+}
+
+function checkElementGlow(tag, style, effectiveBg) {
+  if (!style.boxShadow || style.boxShadow === 'none') return [];
+  return checkGlow({ tag, boxShadow: style.boxShadow, effectiveBg });
+}
+
+// ─── Section 6: Page-Level Checks ───────────────────────────────────────────
+
+// Browser page-level checks — use document/getComputedStyle globals
+
+function checkTypography() {
+  const findings = [];
+
+  // Walk actual text-bearing elements and tally font usage by *computed style*.
+  // This is much more accurate than scanning CSS rules — it ignores rules that
+  // exist in the stylesheet but apply to nothing (e.g. demo classes showing
+  // anti-patterns), and counts what the user actually sees.
+  const fontUsage = new Map(); // primary font name → count of elements
+  let totalTextElements = 0;
+  for (const el of document.querySelectorAll('p, h1, h2, h3, h4, h5, h6, li, td, th, dd, blockquote, figcaption, a, button, label, span')) {
+    // Skip impeccable's own elements
+    if (el.closest && el.closest('.impeccable-overlay, .impeccable-label, .impeccable-banner, .impeccable-tooltip')) continue;
+    // Only count elements that actually have visible direct text
+    const hasText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim().length > 0);
+    if (!hasText) continue;
+    const style = getComputedStyle(el);
+    const ff = style.fontFamily;
+    if (!ff) continue;
+    const stack = ff.split(',').map(f => f.trim().replace(/^['"]|['"]$/g, '').toLowerCase());
+    const primary = stack.find(f => f && !GENERIC_FONTS.has(f));
+    if (!primary) continue;
+    fontUsage.set(primary, (fontUsage.get(primary) || 0) + 1);
+    totalTextElements++;
+  }
+
+  if (totalTextElements >= 20) {
+    // A font is "primary" if it's used by at least 15% of text elements
+    const PRIMARY_THRESHOLD = 0.15;
+    for (const [font, count] of fontUsage) {
+      const share = count / totalTextElements;
+      if (share < PRIMARY_THRESHOLD) continue;
+      if (!OVERUSED_FONTS.has(font)) continue;
+      if (isBrandFontOnOwnDomain(font)) continue;
+      findings.push({ type: 'overused-font', detail: `Primary font: ${font} (${Math.round(share * 100)}% of text)` });
+    }
+
+    // Single-font check: only one distinct primary font across all text
+    if (fontUsage.size === 1) {
+      const only = [...fontUsage.keys()][0];
+      findings.push({ type: 'single-font', detail: `only font used is ${only}` });
+    }
+  }
+
+  const sizes = new Set();
+  for (const el of document.querySelectorAll('h1,h2,h3,h4,h5,h6,p,span,a,li,td,th,label,button,div')) {
+    const fs = parseFloat(getComputedStyle(el).fontSize);
+    if (fs > 0 && fs < 200) sizes.add(Math.round(fs * 10) / 10);
+  }
+  if (sizes.size >= 3) {
+    const sorted = [...sizes].sort((a, b) => a - b);
+    const ratio = sorted[sorted.length - 1] / sorted[0];
+    if (ratio < 2.0) {
+      findings.push({ type: 'flat-type-hierarchy', detail: `Sizes: ${sorted.map(s => s + 'px').join(', ')} (ratio ${ratio.toFixed(1)}:1)` });
+    }
+  }
+
+  return findings;
+}
+
+function isCardLikeDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (SAFE_TAGS.has(tag) || ['input','select','textarea','img','video','canvas','picture'].includes(tag)) return false;
+  const style = getComputedStyle(el);
+  const cls = el.getAttribute('class') || '';
+  const hasShadow = (style.boxShadow && style.boxShadow !== 'none') || /\bshadow(?:-sm|-md|-lg|-xl|-2xl)?\b/.test(cls);
+  const hasBorder = /\bborder\b/.test(cls);
+  const hasRadius = parseFloat(style.borderRadius) > 0 || /\brounded(?:-sm|-md|-lg|-xl|-2xl|-full)?\b/.test(cls);
+  const hasBg = (style.backgroundColor && style.backgroundColor !== 'rgba(0, 0, 0, 0)') || /\bbg-(?:white|gray-\d+|slate-\d+)\b/.test(cls);
+  return isCardLikeFromProps(hasShadow, hasBorder, hasRadius, hasBg);
+}
+
+function checkLayout() {
+  const findings = [];
+  const flaggedEls = new Set();
+
+  for (const el of document.querySelectorAll('*')) {
+    if (!isCardLikeDOM(el) || flaggedEls.has(el)) continue;
+    const cls = el.getAttribute('class') || '';
+    const style = getComputedStyle(el);
+    if (style.position === 'absolute' || style.position === 'fixed') continue;
+    if (/\b(?:dropdown|popover|tooltip|menu|modal|dialog)\b/i.test(cls)) continue;
+    if ((el.textContent?.trim().length || 0) < 10) continue;
+    const rect = el.getBoundingClientRect();
+    if (rect.width < 50 || rect.height < 30) continue;
+
+    let parent = el.parentElement;
+    while (parent) {
+      if (isCardLikeDOM(parent)) { flaggedEls.add(el); break; }
+      parent = parent.parentElement;
+    }
+  }
+
+  for (const el of flaggedEls) {
+    let isAncestor = false;
+    for (const other of flaggedEls) {
+      if (other !== el && el.contains(other)) { isAncestor = true; break; }
+    }
+    if (!isAncestor) findings.push({ type: 'nested-cards', detail: 'Card inside card', el });
+  }
+
+  return findings;
+}
+
+// Node page-level checks — take document/window as parameters
+
+function checkPageTypography(doc, win) {
+  const findings = [];
+
+  const fonts = new Set();
+  const overusedFound = new Set();
+
+  for (const sheet of doc.styleSheets) {
+    let rules;
+    try { rules = sheet.cssRules || sheet.rules; } catch { continue; }
+    if (!rules) continue;
+    for (const rule of rules) {
+      if (rule.type !== 1) continue;
+      const ff = rule.style?.fontFamily;
+      if (!ff) continue;
+      const stack = ff.split(',').map(f => f.trim().replace(/^['"]|['"]$/g, '').toLowerCase());
+      const primary = stack.find(f => f && !GENERIC_FONTS.has(f));
+      if (primary) {
+        fonts.add(primary);
+        if (OVERUSED_FONTS.has(primary)) overusedFound.add(primary);
+      }
+    }
+  }
+
+  // Check Google Fonts links in HTML
+  const html = doc.documentElement?.outerHTML || '';
+  const gfRe = /fonts\.googleapis\.com\/css2?\?family=([^&"'\s]+)/gi;
+  let m;
+  while ((m = gfRe.exec(html)) !== null) {
+    const families = m[1].split('|').map(f => f.split(':')[0].replace(/\+/g, ' ').toLowerCase());
+    for (const f of families) {
+      fonts.add(f);
+      if (OVERUSED_FONTS.has(f)) overusedFound.add(f);
+    }
+  }
+
+  // Also parse raw HTML/style content for font-family (jsdom may not expose all via CSSOM)
+  const ffRe = /font-family\s*:\s*([^;}]+)/gi;
+  let fm;
+  while ((fm = ffRe.exec(html)) !== null) {
+    for (const f of fm[1].split(',').map(f => f.trim().replace(/^['"]|['"]$/g, '').toLowerCase())) {
+      if (f && !GENERIC_FONTS.has(f)) {
+        fonts.add(f);
+        if (OVERUSED_FONTS.has(f)) overusedFound.add(f);
+      }
+    }
+  }
+
+  for (const font of overusedFound) {
+    findings.push({ id: 'overused-font', snippet: `Primary font: ${font}` });
+  }
+
+  // Single font
+  if (fonts.size === 1) {
+    const els = doc.querySelectorAll('*');
+    if (els.length >= 20) {
+      findings.push({ id: 'single-font', snippet: `only font used is ${[...fonts][0]}` });
+    }
+  }
+
+  // Flat type hierarchy
+  const sizes = new Set();
+  const textEls = doc.querySelectorAll('h1, h2, h3, h4, h5, h6, p, span, a, li, td, th, label, button, div');
+  for (const el of textEls) {
+    const fontSize = parseFloat(win.getComputedStyle(el).fontSize);
+    // Filter out sub-8px values (jsdom doesn't resolve relative units properly)
+    if (fontSize >= 8 && fontSize < 200) sizes.add(Math.round(fontSize * 10) / 10);
+  }
+  if (sizes.size >= 3) {
+    const sorted = [...sizes].sort((a, b) => a - b);
+    const ratio = sorted[sorted.length - 1] / sorted[0];
+    if (ratio < 2.0) {
+      findings.push({ id: 'flat-type-hierarchy', snippet: `Sizes: ${sorted.map(s => s + 'px').join(', ')} (ratio ${ratio.toFixed(1)}:1)` });
+    }
+  }
+
+  return findings;
+}
+
+function isCardLike(el, win) {
+  const tag = el.tagName.toLowerCase();
+  if (SAFE_TAGS.has(tag) || ['input', 'select', 'textarea', 'img', 'video', 'canvas', 'picture'].includes(tag)) return false;
+
+  const style = win.getComputedStyle(el);
+  const rawStyle = el.getAttribute?.('style') || '';
+  const cls = el.getAttribute?.('class') || '';
+
+  const hasShadow = (style.boxShadow && style.boxShadow !== 'none') ||
+    /\bshadow(?:-sm|-md|-lg|-xl|-2xl)?\b/.test(cls) || /box-shadow/i.test(rawStyle);
+  const hasBorder = /\bborder\b/.test(cls);
+  const hasRadius = (parseFloat(style.borderRadius) || 0) > 0 ||
+    /\brounded(?:-sm|-md|-lg|-xl|-2xl|-full)?\b/.test(cls) || /border-radius/i.test(rawStyle);
+  const hasBg = /\bbg-(?:white|gray-\d+|slate-\d+)\b/.test(cls) ||
+    /background(?:-color)?\s*:\s*(?!transparent)/i.test(rawStyle);
+
+  return isCardLikeFromProps(hasShadow, hasBorder, hasRadius, hasBg);
+}
+
+function checkPageLayout(doc, win) {
+  const findings = [];
+
+  // Nested cards
+  const allEls = doc.querySelectorAll('*');
+  const flaggedEls = new Set();
+  for (const el of allEls) {
+    if (!isCardLike(el, win)) continue;
+    if (flaggedEls.has(el)) continue;
+
+    const tag = el.tagName.toLowerCase();
+    const cls = el.getAttribute?.('class') || '';
+    const rawStyle = el.getAttribute?.('style') || '';
+
+    if (['pre', 'code'].includes(tag)) continue;
+    if (/\b(?:absolute|fixed)\b/.test(cls) || /position\s*:\s*(?:absolute|fixed)/i.test(rawStyle)) continue;
+    if ((el.textContent?.trim().length || 0) < 10) continue;
+    if (/\b(?:dropdown|popover|tooltip|menu|modal|dialog)\b/i.test(cls)) continue;
+
+    // Walk up to find card-like ancestor
+    let parent = el.parentElement;
+    while (parent) {
+      if (isCardLike(parent, win)) {
+        flaggedEls.add(el);
+        break;
+      }
+      parent = parent.parentElement;
+    }
+  }
+
+  // Only report innermost nested cards
+  for (const el of flaggedEls) {
+    let isAncestorOfFlagged = false;
+    for (const other of flaggedEls) {
+      if (other !== el && el.contains(other)) {
+        isAncestorOfFlagged = true;
+        break;
+      }
+    }
+    if (!isAncestorOfFlagged) {
+      findings.push({ id: 'nested-cards', snippet: `Card inside card (${el.tagName.toLowerCase()})` });
+    }
+  }
+
+  // Everything centered
+  const textEls = doc.querySelectorAll('h1, h2, h3, h4, h5, h6, p, li, div, button');
+  let centeredCount = 0;
+  let totalText = 0;
+  for (const el of textEls) {
+    const hasDirectText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim().length >= 3);
+    if (!hasDirectText) continue;
+    totalText++;
+
+    let cur = el;
+    let isCentered = false;
+    while (cur && cur.nodeType === 1) {
+      const rawStyle = cur.getAttribute?.('style') || '';
+      const cls = cur.getAttribute?.('class') || '';
+      if (/text-align\s*:\s*center/i.test(rawStyle) || /\btext-center\b/.test(cls)) {
+        isCentered = true;
+        break;
+      }
+      if (cur.tagName === 'BODY') break;
+      cur = cur.parentElement;
+    }
+    if (isCentered) centeredCount++;
+  }
+
+  if (totalText >= 5 && centeredCount / totalText > 0.7) {
+    findings.push({
+      id: 'everything-centered',
+      snippet: `${centeredCount}/${totalText} text elements centered (${Math.round(centeredCount / totalText * 100)}%)`,
+    });
+  }
+
+  return findings;
+}
+
+// ─── Section 7: Browser UI (IS_BROWSER only) ────────────────────────────────
+
+if (IS_BROWSER) {
+  // Detect extension mode via the script tag's data attribute or the document element fallback.
+  // currentScript is reliable for synchronously-executing scripts (which our IIFE is).
+  const _myScript = document.currentScript;
+  const EXTENSION_MODE = (_myScript && _myScript.dataset.impeccableExtension === 'true')
+    || document.documentElement.dataset.impeccableExtension === 'true';
+
+  const BRAND_COLOR = 'oklch(55% 0.25 350)';
+  const BRAND_COLOR_HOVER = 'oklch(45% 0.25 350)';
+  const LABEL_BG = BRAND_COLOR;
+  const OUTLINE_COLOR = BRAND_COLOR;
+
+  // Inject hover styles via CSS (more reliable than JS event listeners)
+  const styleEl = document.createElement('style');
+  styleEl.textContent = `
+    @keyframes impeccable-reveal {
+      from { opacity: 0; }
+      to { opacity: 1; }
+    }
+    .impeccable-overlay:not(.impeccable-banner) {
+      pointer-events: none;
+      outline: 2px solid ${OUTLINE_COLOR};
+      border-radius: 4px;
+      transition: outline-color 0.15s ease;
+      animation: impeccable-reveal 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
+      animation-play-state: paused;
+      border-top-left-radius: 0;
+    }
+    .impeccable-overlay.impeccable-visible {
+      animation-play-state: running;
+    }
+    .impeccable-overlay.impeccable-hover {
+      outline-color: ${BRAND_COLOR_HOVER};
+      z-index: 100001 !important;
+    }
+    .impeccable-overlay.impeccable-hover .impeccable-label {
+      background: ${BRAND_COLOR_HOVER};
+    }
+    .impeccable-overlay.impeccable-spotlight {
+      z-index: 100002 !important;
+    }
+    .impeccable-overlay.impeccable-spotlight-dimmed {
+      opacity: 0.15 !important;
+      animation: none !important;
+      filter: blur(3px);
+    }
+    .impeccable-spotlight-backdrop {
+      position: fixed;
+      top: 0; left: 0; right: 0; bottom: 0;
+      backdrop-filter: blur(3px) brightness(0.6);
+      -webkit-backdrop-filter: blur(3px) brightness(0.6);
+      pointer-events: none;
+      z-index: 99998;
+      opacity: 0;
+      outline: none !important;
+      animation: none !important;
+    }
+    .impeccable-spotlight-backdrop.impeccable-visible {
+      opacity: 1;
+    }
+    .impeccable-hidden .impeccable-overlay${EXTENSION_MODE ? '' : ':not(.impeccable-banner)'} {
+      display: none !important;
+    }
+    .impeccable-hidden .impeccable-overlay${EXTENSION_MODE ? '' : ':not(.impeccable-banner)'} {
+      display: none !important;
+    }
+  `;
+  (document.head || document.documentElement).appendChild(styleEl);
+
+  // Spotlight backdrop element (created lazily on first use)
+  let spotlightBackdrop = null;
+  let spotlightTarget = null;
+  let spotlightTimer = null;
+
+  function getSpotlightBackdrop() {
+    if (!spotlightBackdrop) {
+      spotlightBackdrop = document.createElement('div');
+      spotlightBackdrop.className = 'impeccable-spotlight-backdrop';
+      document.body.appendChild(spotlightBackdrop);
+    }
+    return spotlightBackdrop;
+  }
+
+  function updateSpotlightClipPath() {
+    if (!spotlightBackdrop || !spotlightTarget) return;
+    const r = spotlightTarget.getBoundingClientRect();
+    // Match the overlay's outer edge: element rect + 4px (2px overlay offset + 2px outline width)
+    const inset = 4;
+    const radius = 6; // outline border-radius (4) + outline width (2)
+    const x1 = r.left - inset;
+    const y1 = r.top - inset;
+    const x2 = r.right + inset;
+    const y2 = r.bottom + inset;
+    const vw = window.innerWidth;
+    const vh = window.innerHeight;
+    // Outer rect + rounded inner rect (evenodd creates a hole)
+    const path = `M0 0H${vw}V${vh}H0Z M${x1 + radius} ${y1}H${x2 - radius}A${radius} ${radius} 0 0 1 ${x2} ${y1 + radius}V${y2 - radius}A${radius} ${radius} 0 0 1 ${x2 - radius} ${y2}H${x1 + radius}A${radius} ${radius} 0 0 1 ${x1} ${y2 - radius}V${y1 + radius}A${radius} ${radius} 0 0 1 ${x1 + radius} ${y1}Z`;
+    spotlightBackdrop.style.clipPath = `path(evenodd, "${path}")`;
+  }
+
+  function showSpotlight(target) {
+    if (!target || !target.getBoundingClientRect) return;
+    // Respect the spotlightBlur setting: if disabled, don't show the backdrop
+    if (window.__IMPECCABLE_CONFIG__?.spotlightBlur === false) {
+      spotlightTarget = target;
+      return;
+    }
+    spotlightTarget = target;
+    const bd = getSpotlightBackdrop();
+    updateSpotlightClipPath();
+    bd.classList.add('impeccable-visible');
+  }
+
+  function hideSpotlight() {
+    spotlightTarget = null;
+    if (spotlightTimer) { clearTimeout(spotlightTimer); spotlightTimer = null; }
+    if (spotlightBackdrop) spotlightBackdrop.classList.remove('impeccable-visible');
+  }
+
+  function isInViewport(el) {
+    const r = el.getBoundingClientRect();
+    return r.top >= 0 && r.left >= 0 && r.bottom <= window.innerHeight && r.right <= window.innerWidth;
+  }
+
+  // Reposition spotlight on scroll/resize
+  window.addEventListener('scroll', () => {
+    if (spotlightTarget) updateSpotlightClipPath();
+  }, { passive: true });
+  window.addEventListener('resize', () => {
+    if (spotlightTarget) updateSpotlightClipPath();
+  });
+
+  const overlays = [];
+  const TYPE_LABELS = {};
+  const RULE_CATEGORY = {};
+  for (const ap of ANTIPATTERNS) {
+    TYPE_LABELS[ap.id] = ap.name.toLowerCase();
+    RULE_CATEGORY[ap.id] = ap.category || 'quality';
+  }
+
+  function isInFixedContext(el) {
+    let p = el;
+    while (p && p !== document.body) {
+      if (getComputedStyle(p).position === 'fixed') return true;
+      p = p.parentElement;
+    }
+    return false;
+  }
+
+  function positionOverlay(overlay) {
+    const el = overlay._targetEl;
+    if (!el) return;
+    const rect = el.getBoundingClientRect();
+    if (overlay._isFixed) {
+      // Viewport-relative coords for fixed targets
+      overlay.style.top = `${rect.top - 2}px`;
+      overlay.style.left = `${rect.left - 2}px`;
+    } else {
+      // Document-relative coords for normal targets
+      overlay.style.top = `${rect.top + scrollY - 2}px`;
+      overlay.style.left = `${rect.left + scrollX - 2}px`;
+    }
+    overlay.style.width = `${rect.width + 4}px`;
+    overlay.style.height = `${rect.height + 4}px`;
+  }
+
+  function repositionOverlays() {
+    for (const o of overlays) {
+      if (!o._targetEl || o.classList.contains('impeccable-banner')) continue;
+      // Skip overlays whose target is currently hidden (display: none on the overlay)
+      if (o.style.display === 'none') continue;
+      positionOverlay(o);
+    }
+  }
+
+  let resizeRAF;
+  const onResize = () => {
+    cancelAnimationFrame(resizeRAF);
+    resizeRAF = requestAnimationFrame(repositionOverlays);
+  };
+  window.addEventListener('resize', onResize);
+  // Reposition on scroll too -- catches sticky/parallax shifts
+  window.addEventListener('scroll', onResize, { passive: true });
+  // Reposition when body resizes (lazy-loaded images, dynamic content, fonts loading)
+  if (typeof ResizeObserver !== 'undefined') {
+    const bodyResizeObserver = new ResizeObserver(onResize);
+    bodyResizeObserver.observe(document.body);
+  }
+
+  // Track target element visibility via IntersectionObserver.
+  // Uses a huge rootMargin so all *rendered* elements count as intersecting,
+  // while display:none / closed <details> / hidden modals etc. do not.
+  // This is event-driven -- no polling needed.
+  let overlayIndex = 0;
+  const visibilityObserver = new IntersectionObserver((entries) => {
+    for (const entry of entries) {
+      const overlay = entry.target._impeccableOverlay;
+      if (!overlay) continue;
+      if (entry.isIntersecting) {
+        overlay.style.display = '';
+        positionOverlay(overlay);
+        if (!overlay._revealed) {
+          overlay._revealed = true;
+          if (firstScanDone) {
+            // Subsequent reveals (re-scans, scroll-into-view): instant, no animation
+            overlay.style.animation = 'none';
+          } else {
+            // Initial scan: staggered cascade reveal
+            overlay.style.animationDelay = `${Math.min((overlay._staggerIndex || 0) * 60, 600)}ms`;
+          }
+          requestAnimationFrame(() => {
+            overlay.classList.add('impeccable-visible');
+            if (overlay._checkLabel) overlay._checkLabel();
+          });
+        }
+      } else {
+        overlay.style.display = 'none';
+      }
+    }
+  }, { rootMargin: '99999px' });
+
+  // Reposition overlays after CSS transitions end (e.g. reveal animations).
+  // Listens at document level so it catches transitions on ancestor elements
+  // (the transform may be on a parent, not the flagged element itself).
+  document.addEventListener('transitionend', (e) => {
+    if (e.propertyName !== 'transform') return;
+    for (const o of overlays) {
+      if (!o._targetEl || o.classList.contains('impeccable-banner') || o.style.display === 'none') continue;
+      if (e.target === o._targetEl || e.target.contains(o._targetEl)) {
+        positionOverlay(o);
+      }
+    }
+  });
+
+  const highlight = function(el, findings) {
+    const hasSlop = findings.some(f => RULE_CATEGORY[f.type || f.id] === 'slop');
+
+    const fixed = isInFixedContext(el);
+    const rect = el.getBoundingClientRect();
+    const outline = document.createElement('div');
+    outline.className = 'impeccable-overlay';
+    outline._targetEl = el;
+    outline._isFixed = fixed;
+    Object.assign(outline.style, {
+      position: fixed ? 'fixed' : 'absolute',
+      top: fixed ? `${rect.top - 2}px` : `${rect.top + scrollY - 2}px`,
+      left: fixed ? `${rect.left - 2}px` : `${rect.left + scrollX - 2}px`,
+      width: `${rect.width + 4}px`, height: `${rect.height + 4}px`,
+      zIndex: '99999', boxSizing: 'border-box',
+    });
+
+    // Build per-finding label entries: ✦ prefix for slop
+    const entries = findings.map(f => {
+      const name = TYPE_LABELS[f.type || f.id] || f.type || f.id;
+      const prefix = RULE_CATEGORY[f.type || f.id] === 'slop' ? '\u2726 ' : '';
+      return { name: prefix + name, detail: f.detail || f.snippet };
+    });
+    const allText = entries.map(e => e.name).join(', ');
+
+    const label = document.createElement('div');
+    label.className = 'impeccable-label';
+    Object.assign(label.style, {
+      position: 'absolute', bottom: '100%', left: '-2px',
+      display: 'flex', alignItems: 'center',
+      whiteSpace: 'nowrap',
+      fontSize: '11px', fontWeight: '600', letterSpacing: '0.02em',
+      color: 'white', lineHeight: '14px',
+      background: LABEL_BG,
+      fontFamily: 'system-ui, sans-serif',
+      borderRadius: '4px 4px 0 0',
+    });
+
+    const textSpan = document.createElement('span');
+    textSpan.style.padding = '3px 8px';
+    textSpan.textContent = allText;
+    label.appendChild(textSpan);
+
+    // State for cycling mode
+    let cycleMode = false;
+    let cycleIndex = 0;
+    let isHovered = false;
+    let prevBtn, nextBtn;
+
+    function updateCycleText() {
+      const e = entries[cycleIndex];
+      textSpan.textContent = isHovered ? e.detail : e.name;
+    }
+
+    function enableCycleMode() {
+      if (cycleMode || entries.length < 2) return;
+      cycleMode = true;
+
+      const btnStyle = {
+        background: 'none', border: 'none', color: 'rgba(255,255,255,0.7)',
+        fontSize: '11px', cursor: 'pointer', padding: '3px 4px',
+        fontFamily: 'system-ui, sans-serif', lineHeight: '14px',
+        pointerEvents: 'auto',
+      };
+
+      const navGroup = document.createElement('span');
+      Object.assign(navGroup.style, {
+        display: 'inline-flex', alignItems: 'center', flexShrink: '0',
+      });
+
+      prevBtn = document.createElement('button');
+      prevBtn.textContent = '\u2039';
+      Object.assign(prevBtn.style, btnStyle);
+      prevBtn.style.paddingLeft = '6px';
+      prevBtn.addEventListener('click', (e) => {
+        e.stopPropagation();
+        cycleIndex = (cycleIndex - 1 + entries.length) % entries.length;
+        updateCycleText();
+      });
+
+      nextBtn = document.createElement('button');
+      nextBtn.textContent = '\u203A';
+      Object.assign(nextBtn.style, btnStyle);
+      nextBtn.style.paddingRight = '2px';
+      nextBtn.addEventListener('click', (e) => {
+        e.stopPropagation();
+        cycleIndex = (cycleIndex + 1) % entries.length;
+        updateCycleText();
+      });
+
+      navGroup.appendChild(prevBtn);
+      navGroup.appendChild(nextBtn);
+      label.insertBefore(navGroup, textSpan);
+      textSpan.style.padding = '3px 8px 3px 4px';
+      updateCycleText();
+    }
+
+    outline.appendChild(label);
+
+    // Start hidden; the IntersectionObserver will show it once the target is rendered
+    outline.style.display = 'none';
+    outline._staggerIndex = overlayIndex++;
+    el._impeccableOverlay = outline;
+    visibilityObserver.observe(el);
+
+    // After first paint, check label width vs outline
+    outline._checkLabel = () => {
+      if (entries.length > 1 && label.offsetWidth > outline.offsetWidth) {
+        enableCycleMode();
+      }
+    };
+
+    // Hover: show detail text, darken
+    el.addEventListener('mouseenter', () => {
+      isHovered = true;
+      outline.classList.add('impeccable-hover');
+      outline.style.outlineColor = BRAND_COLOR_HOVER;
+      label.style.background = BRAND_COLOR_HOVER;
+      if (cycleMode) {
+        updateCycleText();
+      } else {
+        textSpan.textContent = entries.map(e => e.detail).join(' | ');
+      }
+    });
+    el.addEventListener('mouseleave', () => {
+      isHovered = false;
+      outline.classList.remove('impeccable-hover');
+      outline.style.outlineColor = '';
+      label.style.background = LABEL_BG;
+      if (cycleMode) {
+        updateCycleText();
+      } else {
+        textSpan.textContent = allText;
+      }
+    });
+
+    document.body.appendChild(outline);
+    overlays.push(outline);
+  };
+
+  const showPageBanner = function(findings) {
+    if (!findings.length) return;
+    const banner = document.createElement('div');
+    banner.className = 'impeccable-overlay impeccable-banner';
+    Object.assign(banner.style, {
+      position: 'fixed', top: '0', left: '0', right: '0', zIndex: '100000',
+      background: LABEL_BG, color: 'white',
+      fontFamily: 'system-ui, sans-serif', fontSize: '13px',
+      display: 'flex', alignItems: 'center', pointerEvents: 'auto',
+      height: '36px', overflow: 'hidden', maxWidth: '100vw',
+      transform: 'translateY(-100%)',
+      transition: 'transform 0.4s cubic-bezier(0.16, 1, 0.3, 1)',
+    });
+    requestAnimationFrame(() => requestAnimationFrame(() => {
+      banner.style.transform = 'translateY(0)';
+    }));
+
+    // Scrollable findings area
+    const scrollArea = document.createElement('div');
+    Object.assign(scrollArea.style, {
+      flex: '1', minWidth: '0', overflowX: 'auto', overflowY: 'hidden',
+      display: 'flex', gap: '8px', alignItems: 'center',
+      padding: '0 12px', scrollSnapType: 'x mandatory',
+      scrollbarWidth: 'none',
+    });
+    for (const f of findings) {
+      const prefix = RULE_CATEGORY[f.type] === 'slop' ? '\u2726 ' : '';
+      const tag = document.createElement('span');
+      tag.textContent = `${prefix}${TYPE_LABELS[f.type] || f.type}: ${f.detail}`;
+      Object.assign(tag.style, {
+        background: 'rgba(255,255,255,0.15)', padding: '2px 8px',
+        borderRadius: '3px', fontSize: '12px', fontFamily: 'ui-monospace, monospace',
+        whiteSpace: 'nowrap', flexShrink: '0', scrollSnapAlign: 'start',
+      });
+      scrollArea.appendChild(tag);
+    }
+    banner.appendChild(scrollArea);
+
+    // Controls area (only in standalone mode, not extension)
+    if (!EXTENSION_MODE) {
+      const controls = document.createElement('div');
+      Object.assign(controls.style, {
+        display: 'flex', alignItems: 'center', gap: '2px',
+        padding: '0 8px', flexShrink: '0',
+      });
+
+      // Toggle visibility button
+      const toggle = document.createElement('button');
+      toggle.textContent = '\u25C9'; // circle with dot (visible state)
+      toggle.title = 'Toggle overlay visibility';
+      Object.assign(toggle.style, {
+        background: 'none', border: 'none',
+        color: 'white', fontSize: '16px', cursor: 'pointer', padding: '0 4px',
+        opacity: '0.85', transition: 'opacity 0.15s',
+      });
+      let overlaysVisible = true;
+      toggle.addEventListener('click', () => {
+        overlaysVisible = !overlaysVisible;
+        document.body.classList.toggle('impeccable-hidden', !overlaysVisible);
+        toggle.textContent = overlaysVisible ? '\u25C9' : '\u25CB'; // filled vs empty circle
+        toggle.style.opacity = overlaysVisible ? '0.85' : '0.5';
+      });
+      controls.appendChild(toggle);
+
+      // Close button
+      const close = document.createElement('button');
+      close.textContent = '\u00d7';
+      close.title = 'Dismiss banner';
+      Object.assign(close.style, {
+        background: 'none', border: 'none',
+        color: 'white', fontSize: '18px', cursor: 'pointer', padding: '0 4px',
+      });
+      close.addEventListener('click', () => banner.remove());
+      controls.appendChild(close);
+
+      banner.appendChild(controls);
+    }
+    document.body.appendChild(banner);
+    overlays.push(banner);
+  };
+
+  // Heuristic for skipping CSS-in-JS hashed class names like "css-1a2b3c" or "_2x4hG_".
+  // These change between builds and produce brittle, ugly selectors.
+  function isLikelyHashedClass(c) {
+    if (!c) return true;
+    if (/^(css|sc|emotion|jsx|module)-[\w-]{4,}$/i.test(c)) return true;
+    if (/^_[\w-]{5,}$/.test(c)) return true;
+    if (/^[a-z0-9]{6,}$/i.test(c) && /\d/.test(c)) return true;
+    return false;
+  }
+
+  function buildSelectorSegment(el) {
+    const tag = el.tagName.toLowerCase();
+    let sel = tag;
+
+    if (el.classList && el.classList.length > 0) {
+      const classes = [...el.classList]
+        .filter(c => !c.startsWith('impeccable-') && !isLikelyHashedClass(c))
+        .slice(0, 2);
+      if (classes.length > 0) {
+        sel += '.' + classes.map(c => CSS.escape(c)).join('.');
+      }
+    }
+
+    // Disambiguate among siblings only if the parent has multiple matches
+    const parent = el.parentElement;
+    if (parent) {
+      try {
+        const matching = parent.querySelectorAll(':scope > ' + sel);
+        if (matching.length > 1) {
+          const sameType = [...parent.children].filter(c => c.tagName === el.tagName);
+          const idx = sameType.indexOf(el) + 1;
+          sel += `:nth-of-type(${idx})`;
+        }
+      } catch {
+        const idx = [...parent.children].indexOf(el) + 1;
+        sel = `${tag}:nth-child(${idx})`;
+      }
+    }
+    return sel;
+  }
+
+  function generateSelector(el) {
+    if (el === document.body) return 'body';
+    if (el === document.documentElement) return 'html';
+    if (el.id) return '#' + CSS.escape(el.id);
+
+    const parts = [];
+    let current = el;
+    let depth = 0;
+    const MAX_DEPTH = 10;
+
+    while (current && current !== document.body && current !== document.documentElement && depth < MAX_DEPTH) {
+      parts.unshift(buildSelectorSegment(current));
+
+      // Anchor on an ancestor's ID and stop walking up
+      if (current.id) {
+        parts[0] = '#' + CSS.escape(current.id);
+        break;
+      }
+
+      // Stop as soon as the partial selector uniquely identifies the target
+      const trySelector = parts.join(' > ');
+      try {
+        const matches = document.querySelectorAll(trySelector);
+        if (matches.length === 1 && matches[0] === el) {
+          return trySelector;
+        }
+      } catch { /* invalid selector — keep walking */ }
+
+      current = current.parentElement;
+      depth++;
+    }
+
+    return parts.join(' > ');
+  }
+
+  function isElementHidden(el) {
+    if (!el || el === document.body || el === document.documentElement) return false;
+    if (typeof el.checkVisibility === 'function') return !el.checkVisibility({ checkOpacity: false, checkVisibilityCSS: true });
+    // Fallback: zero size or no offsetParent (covers display:none and detached subtrees)
+    return el.offsetWidth === 0 && el.offsetHeight === 0;
+  }
+
+  function serializeFindings(allFindings) {
+    return allFindings.map(({ el, findings }) => ({
+      selector: generateSelector(el),
+      tagName: el.tagName?.toLowerCase() || 'unknown',
+      rect: (el !== document.body && el !== document.documentElement && el.getBoundingClientRect)
+        ? el.getBoundingClientRect().toJSON() : null,
+      isPageLevel: el === document.body || el === document.documentElement,
+      isHidden: isElementHidden(el),
+      findings: findings.map(f => {
+        const ap = ANTIPATTERNS.find(a => a.id === (f.type || f.id));
+        return {
+          type: f.type || f.id,
+          category: ap ? ap.category : 'quality',
+          detail: f.detail || f.snippet,
+          name: ap ? ap.name : (f.type || f.id),
+          description: ap ? ap.description : '',
+        };
+      }),
+    }));
+  }
+
+  const printSummary = function(allFindings) {
+    if (allFindings.length === 0) {
+      console.log('%c[impeccable] No anti-patterns found.', 'color: #22c55e; font-weight: bold');
+      return;
+    }
+    console.group(
+      `%c[impeccable] ${allFindings.length} anti-pattern${allFindings.length === 1 ? '' : 's'} found`,
+      'color: oklch(60% 0.25 350); font-weight: bold'
+    );
+    for (const { el, findings } of allFindings) {
+      for (const f of findings) {
+        console.log(`%c${f.type || f.id}%c ${f.detail || f.snippet}`,
+          'color: oklch(55% 0.25 350); font-weight: bold', 'color: inherit', el);
+      }
+    }
+    console.groupEnd();
+  };
+
+  let firstScanDone = false;
+  const scan = function() {
+    for (const o of overlays) o.remove();
+    overlays.length = 0;
+    visibilityObserver.disconnect();
+    overlayIndex = 0;
+    const allFindings = [];
+    const _disabled = EXTENSION_MODE ? (window.__IMPECCABLE_CONFIG__?.disabledRules || []) : [];
+    const _ruleOk = (id) => !_disabled.length || !_disabled.includes(id);
+
+    for (const el of document.querySelectorAll('*')) {
+      // Skip impeccable's own elements and any descendants (overlays, labels, banner, nav buttons)
+      if (el.closest('.impeccable-overlay, .impeccable-label, .impeccable-banner, .impeccable-tooltip')) continue;
+      // Skip browser extension elements (Claude, etc.)
+      const elId = el.id || '';
+      if (elId.startsWith('claude-') || elId.startsWith('cic-')) continue;
+      // Skip html/body -- page-level findings go in the banner, not a full-page overlay
+      if (el === document.body || el === document.documentElement) continue;
+
+      const findings = [
+        ...checkElementBordersDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementColorsDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementMotionDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementGlowDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementAIPaletteDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementIconTileDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementQualityDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+      ].filter(f => _ruleOk(f.type));
+
+      if (findings.length > 0) {
+        highlight(el, findings);
+        allFindings.push({ el, findings });
+      }
+    }
+
+    const pageLevelFindings = [];
+
+    const typoFindings = checkTypography().filter(f => _ruleOk(f.type));
+    if (typoFindings.length > 0) {
+      pageLevelFindings.push(...typoFindings);
+      allFindings.push({ el: document.body, findings: typoFindings });
+    }
+
+    const layoutFindings = checkLayout().filter(f => _ruleOk(f.type));
+    for (const f of layoutFindings) {
+      const el = f.el || document.body;
+      delete f.el;
+      // Merge into existing overlay if this element already has one
+      const existing = el._impeccableOverlay;
+      if (existing) {
+        const nameRow = existing.querySelector('.impeccable-label-name');
+        const detailRow = existing.querySelector('.impeccable-label-detail');
+        const newType = TYPE_LABELS[f.type] || f.type;
+        if (nameRow) nameRow.textContent += ', ' + newType;
+        if (detailRow) detailRow.textContent += ' | ' + (f.detail || '');
+      } else {
+        highlight(el, [f]);
+      }
+      allFindings.push({ el, findings: [f] });
+    }
+
+    // Page-level quality checks (headings, etc.)
+    const qualityFindings = checkPageQualityDOM().filter(f => _ruleOk(f.type));
+    if (qualityFindings.length > 0) {
+      pageLevelFindings.push(...qualityFindings);
+      allFindings.push({ el: document.body, findings: qualityFindings });
+    }
+
+    // Regex-on-HTML checks (shared with Node)
+    const htmlPatternFindings = checkHtmlPatterns(document.documentElement.outerHTML);
+    if (htmlPatternFindings.length > 0) {
+      const mapped = htmlPatternFindings.map(f => ({ type: f.id, detail: f.snippet })).filter(f => _ruleOk(f.type));
+      pageLevelFindings.push(...mapped);
+      allFindings.push({ el: document.body, findings: mapped });
+    }
+
+    if (pageLevelFindings.length > 0) {
+      showPageBanner(pageLevelFindings);
+    }
+
+    if (!EXTENSION_MODE) printSummary(allFindings);
+
+    // In extension mode, post serialized results for the DevTools panel
+    if (EXTENSION_MODE) {
+      window.postMessage({
+        source: 'impeccable-results',
+        findings: serializeFindings(allFindings),
+        count: allFindings.length,
+      }, '*');
+    }
+
+    // After this scan completes, all subsequent reveals are instant (no stagger, no animation)
+    setTimeout(() => { firstScanDone = true; }, 1000);
+
+    return allFindings;
+  };
+
+  if (EXTENSION_MODE) {
+    // Extension mode: listen for commands, don't auto-scan
+    window.addEventListener('message', (e) => {
+      if (e.source !== window || !e.data || e.data.source !== 'impeccable-command') return;
+      if (e.data.action === 'scan') {
+        if (e.data.config) window.__IMPECCABLE_CONFIG__ = e.data.config;
+        scan();
+      }
+      if (e.data.action === 'toggle-overlays') {
+        const visible = !document.body.classList.contains('impeccable-hidden');
+        document.body.classList.toggle('impeccable-hidden', visible);
+        window.postMessage({ source: 'impeccable-overlays-toggled', visible: !visible }, '*');
+      }
+      if (e.data.action === 'remove') {
+        for (const o of overlays) o.remove();
+        overlays.length = 0;
+        visibilityObserver.disconnect();
+        styleEl.remove();
+        if (spotlightBackdrop) { spotlightBackdrop.remove(); spotlightBackdrop = null; }
+        document.body.classList.remove('impeccable-hidden');
+      }
+      if (e.data.action === 'highlight') {
+        if (spotlightTimer) { clearTimeout(spotlightTimer); spotlightTimer = null; }
+        try {
+          const target = e.data.selector ? document.querySelector(e.data.selector) : null;
+          if (target) {
+            // Scroll first so positionOverlay reads the post-scroll rect
+            if (!isInViewport(target) && target.scrollIntoView) {
+              target.scrollIntoView({ behavior: 'instant', block: 'center' });
+            }
+            for (const o of overlays) {
+              if (o.classList.contains('impeccable-banner')) continue;
+              const isMatch = o._targetEl === target;
+              o.classList.toggle('impeccable-spotlight', isMatch);
+              o.classList.toggle('impeccable-spotlight-dimmed', !isMatch);
+              if (isMatch) {
+                // Force the matching overlay visible immediately, don't wait for IntersectionObserver
+                o.style.display = '';
+                o.style.animation = 'none';
+                o.classList.add('impeccable-visible');
+                o._revealed = true;
+                positionOverlay(o);
+              }
+            }
+            showSpotlight(target);
+          }
+        } catch { /* invalid selector */ }
+      }
+      if (e.data.action === 'unhighlight') {
+        hideSpotlight();
+        for (const o of overlays) {
+          o.classList.remove('impeccable-spotlight');
+          o.classList.remove('impeccable-spotlight-dimmed');
+        }
+      }
+    });
+    window.postMessage({ source: 'impeccable-ready' }, '*');
+  } else {
+    if (document.readyState === 'loading') {
+      document.addEventListener('DOMContentLoaded', () => setTimeout(scan, 100));
+    } else {
+      setTimeout(scan, 100);
+    }
+  }
+
+  window.impeccableScan = scan;
+}
+
+// ─── Section 8: Node Engine ─────────────────────────────────────────────────
+
+// ─── Section 9: Exports ─────────────────────────────────────────────────────
+
+})();

src/detect-antipatterns.mjs 🔗

@@ -0,0 +1,3590 @@
+#!/usr/bin/env node
+
+/**
+ * Anti-Pattern Detector for Impeccable
+ * Copyright (c) 2026 Paul Bakaus
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Universal file — auto-detects environment (browser vs Node) and adapts.
+ *
+ * Node usage:
+ *   node detect-antipatterns.mjs [file-or-dir...]   # jsdom for HTML, regex for rest
+ *   node detect-antipatterns.mjs https://...         # Puppeteer (auto)
+ *   node detect-antipatterns.mjs --fast [files...]   # regex-only (skip jsdom)
+ *   node detect-antipatterns.mjs --json              # JSON output
+ *
+ * Browser usage:
+ *   <script src="detect-antipatterns-browser.js"></script>
+ *   Re-scan: window.impeccableScan()
+ *
+ * Exit codes: 0 = clean, 2 = findings
+ */
+
+// ─── Environment ────────────────────────────────────────────────────────────
+
+const IS_BROWSER = typeof window !== 'undefined';
+const IS_NODE = !IS_BROWSER;
+
+// @browser-strip-start
+let fs, path;
+if (!IS_BROWSER) {
+  fs = (await import('node:fs')).default;
+  path = (await import('node:path')).default;
+}
+// @browser-strip-end
+
+// ─── Section 1: Constants ───────────────────────────────────────────────────
+
+const SAFE_TAGS = new Set([
+  'blockquote', 'nav', 'a', 'input', 'textarea', 'select',
+  'pre', 'code', 'span', 'th', 'td', 'tr', 'li', 'label',
+  'button', 'hr', 'html', 'head', 'body', 'script', 'style',
+  'link', 'meta', 'title', 'br', 'img', 'svg', 'path', 'circle',
+  'rect', 'line', 'polyline', 'polygon', 'g', 'defs', 'use',
+]);
+
+// Per-check safe-tags override for the border (side-tab / border-accent)
+// rule. We intentionally re-allow <label> here because card-shaped clickable
+// labels (e.g. .checklist-item wrapping a checkbox + content) are one of the
+// canonical side-tab anti-pattern shapes and must be detected. The rule's
+// other preconditions (non-neutral color, width >= 2px on a single side,
+// radius > 0 or width >= 3, element size >= 20x20 in the browser path)
+// already filter out plain inline form labels so this does not introduce
+// false positives. See modern-color-borders.html for the test matrix.
+const BORDER_SAFE_TAGS = new Set(
+  [...SAFE_TAGS].filter(t => t !== 'label')
+);
+
+const OVERUSED_FONTS = new Set([
+  'inter', 'roboto', 'open sans', 'lato', 'montserrat', 'arial', 'helvetica',
+]);
+
+// Brand-associated fonts: don't flag these as "overused" on the brand's own domains.
+// Keys are font names, values are arrays of hostname suffixes where the font is allowed.
+const GOOGLE_DOMAINS = [
+  'google.com', 'youtube.com', 'android.com', 'chromium.org',
+  'chrome.com', 'web.dev', 'gstatic.com', 'firebase.google.com',
+];
+const BRAND_FONT_DOMAINS = {
+  'roboto': GOOGLE_DOMAINS,
+  'google sans': GOOGLE_DOMAINS,
+  'product sans': GOOGLE_DOMAINS,
+};
+
+function isBrandFontOnOwnDomain(font) {
+  if (typeof location === 'undefined') return false;
+  const allowed = BRAND_FONT_DOMAINS[font];
+  if (!allowed) return false;
+  const host = location.hostname.toLowerCase();
+  return allowed.some(suffix => host === suffix || host.endsWith('.' + suffix));
+}
+
+const GENERIC_FONTS = new Set([
+  'serif', 'sans-serif', 'monospace', 'cursive', 'fantasy',
+  'system-ui', 'ui-serif', 'ui-sans-serif', 'ui-monospace', 'ui-rounded',
+  '-apple-system', 'blinkmacsystemfont', 'segoe ui',
+  'inherit', 'initial', 'unset', 'revert',
+]);
+
+const ANTIPATTERNS = [
+  // ── AI slop: tells that something was AI-generated ──
+  {
+    id: 'side-tab',
+    category: 'slop',
+    name: 'Side-tab accent border',
+    description:
+      'Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.',
+    skillSection: 'Visual Details',
+    skillGuideline: 'colored accent stripe',
+  },
+  {
+    id: 'border-accent-on-rounded',
+    category: 'slop',
+    name: 'Border accent on rounded element',
+    description:
+      'Thick accent border on a rounded card — the border clashes with the rounded corners. Remove the border or the border-radius.',
+    skillSection: 'Visual Details',
+    skillGuideline: 'colored accent stripe',
+  },
+  {
+    id: 'overused-font',
+    category: 'slop',
+    name: 'Overused font',
+    description:
+      'Inter, Roboto, Open Sans, Lato, Montserrat, and Arial are used on millions of sites. Choose a distinctive font that gives your interface personality.',
+    skillSection: 'Typography',
+    skillGuideline: 'overused fonts like Inter',
+  },
+  {
+    id: 'single-font',
+    category: 'slop',
+    name: 'Single font for everything',
+    description:
+      'Only one font family is used for the entire page. Pair a distinctive display font with a refined body font to create typographic hierarchy.',
+    skillSection: 'Typography',
+    skillGuideline: 'only one font family for the entire page',
+  },
+  {
+    id: 'flat-type-hierarchy',
+    category: 'slop',
+    name: 'Flat type hierarchy',
+    description:
+      'Font sizes are too close together — no clear visual hierarchy. Use fewer sizes with more contrast (aim for at least a 1.25 ratio between steps).',
+    skillSection: 'Typography',
+    skillGuideline: 'flat type hierarchy',
+  },
+  {
+    id: 'gradient-text',
+    category: 'slop',
+    name: 'Gradient text',
+    description:
+      'Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'gradient text for',
+  },
+  {
+    id: 'ai-color-palette',
+    category: 'slop',
+    name: 'AI color palette',
+    description:
+      'Purple/violet gradients and cyan-on-dark are the most recognizable tells of AI-generated UIs. Choose a distinctive, intentional palette.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'AI color palette',
+  },
+  {
+    id: 'nested-cards',
+    category: 'slop',
+    name: 'Nested cards',
+    description:
+      'Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.',
+    skillSection: 'Layout & Space',
+    skillGuideline: 'Nest cards inside cards',
+  },
+  {
+    id: 'monotonous-spacing',
+    category: 'slop',
+    name: 'Monotonous spacing',
+    description:
+      'The same spacing value used everywhere — no rhythm, no variation. Use tight groupings for related items and generous separations between sections.',
+    skillSection: 'Layout & Space',
+    skillGuideline: 'same spacing everywhere',
+  },
+  {
+    id: 'everything-centered',
+    category: 'slop',
+    name: 'Everything centered',
+    description:
+      'Every text element is center-aligned. Left-aligned text with asymmetric layouts feels more designed. Center only hero sections and CTAs.',
+    skillSection: 'Layout & Space',
+    skillGuideline: 'Center everything',
+  },
+  {
+    id: 'bounce-easing',
+    category: 'slop',
+    name: 'Bounce or elastic easing',
+    description:
+      'Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.',
+    skillSection: 'Motion',
+    skillGuideline: 'bounce or elastic easing',
+  },
+  {
+    id: 'dark-glow',
+    category: 'slop',
+    name: 'Dark mode with glowing accents',
+    description:
+      'Dark backgrounds with colored box-shadow glows are the default "cool" look of AI-generated UIs. Use subtle, purposeful lighting instead — or skip the dark theme entirely.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'dark mode with glowing accents',
+  },
+  {
+    id: 'icon-tile-stack',
+    category: 'slop',
+    name: 'Icon tile stacked above heading',
+    description:
+      'A small rounded-square icon container above a heading is the universal AI feature-card template — every generator outputs this exact shape. Try a side-by-side icon and heading, or let the icon sit in flow without its own container.',
+    skillSection: 'Typography',
+    skillGuideline: 'large icons with rounded corners above every heading',
+  },
+
+  // ── Quality: general design and accessibility issues ──
+  {
+    id: 'pure-black-white',
+    category: 'quality',
+    name: 'Pure black background',
+    description:
+      'Pure #000000 as a background color looks harsh and unnatural. Tint it slightly toward your brand hue (e.g., oklch(12% 0.01 250)) for a more refined feel.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'pure black (#000)',
+  },
+  {
+    id: 'gray-on-color',
+    category: 'quality',
+    name: 'Gray text on colored background',
+    description:
+      'Gray text looks washed out on colored backgrounds. Use a darker shade of the background color instead, or white/near-white for contrast.',
+    skillSection: 'Color & Contrast',
+    skillGuideline: 'gray text on colored backgrounds',
+  },
+  {
+    id: 'low-contrast',
+    category: 'quality',
+    name: 'Low contrast text',
+    description:
+      'Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.',
+  },
+  {
+    id: 'layout-transition',
+    category: 'quality',
+    name: 'Layout property animation',
+    description:
+      'Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead, or grid-template-rows for height animations.',
+    skillSection: 'Motion',
+    skillGuideline: 'Animate layout properties',
+  },
+  {
+    id: 'line-length',
+    category: 'quality',
+    name: 'Line length too long',
+    description:
+      'Text lines wider than ~80 characters are hard to read. The eye loses its place tracking back to the start of the next line. Add a max-width (65ch to 75ch) to text containers.',
+    skillSection: 'Layout & Space',
+    skillGuideline: 'wrap beyond ~80 characters',
+  },
+  {
+    id: 'cramped-padding',
+    category: 'quality',
+    name: 'Cramped padding',
+    description:
+      'Text is too close to the edge of its container. Add at least 8px (ideally 12-16px) of padding inside bordered or colored containers.',
+  },
+  {
+    id: 'tight-leading',
+    category: 'quality',
+    name: 'Tight line height',
+    description:
+      'Line height below 1.3x the font size makes multi-line text hard to read. Use 1.5 to 1.7 for body text so lines have room to breathe.',
+  },
+  {
+    id: 'skipped-heading',
+    category: 'quality',
+    name: 'Skipped heading level',
+    description:
+      'Heading levels should not skip (e.g. h1 then h3 with no h2). Screen readers use heading hierarchy for navigation. Skipping levels breaks the document outline.',
+  },
+  {
+    id: 'justified-text',
+    category: 'quality',
+    name: 'Justified text',
+    description:
+      'Justified text without hyphenation creates uneven word spacing ("rivers of white"). Use text-align: left for body text, or enable hyphens: auto if you must justify.',
+  },
+  {
+    id: 'tiny-text',
+    category: 'quality',
+    name: 'Tiny body text',
+    description:
+      'Body text below 12px is hard to read, especially on high-DPI screens. Use at least 14px for body content, 16px is ideal.',
+  },
+  {
+    id: 'all-caps-body',
+    category: 'quality',
+    name: 'All-caps body text',
+    description:
+      'Long passages in uppercase are hard to read. We recognize words by shape (ascenders and descenders), which all-caps removes. Reserve uppercase for short labels and headings.',
+    skillSection: 'Typography',
+    skillGuideline: 'long body passages in uppercase',
+  },
+  {
+    id: 'wide-tracking',
+    category: 'quality',
+    name: 'Wide letter spacing on body text',
+    description:
+      'Letter spacing above 0.05em on body text disrupts natural character groupings and slows reading. Reserve wide tracking for short uppercase labels only.',
+  },
+];
+
+// ─── Section 2: Color Utilities ─────────────────────────────────────────────
+
+function isNeutralColor(color) {
+  if (!color || color === 'transparent') return true;
+
+  // rgb/rgba — use channel spread. Threshold 30 ≈ 11.7% of the 0–255 range.
+  const rgb = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
+  if (rgb) {
+    return (Math.max(+rgb[1], +rgb[2], +rgb[3]) - Math.min(+rgb[1], +rgb[2], +rgb[3])) < 30;
+  }
+
+  // oklch()/lch() — chroma is the second numeric component.
+  // oklch chroma is ~0–0.4 in sRGB gamut; >= 0.02 reads as tinted, not gray.
+  // lch chroma is ~0–150; >= 3 reads as tinted. jsdom emits both formats
+  // literally (it does NOT convert them to rgb).
+  const oklch = color.match(/oklch\(\s*[\d.%-]+\s+([\d.-]+)/i);
+  if (oklch) return parseFloat(oklch[1]) < 0.02;
+  const lch = color.match(/lch\(\s*[\d.%-]+\s+([\d.-]+)/i);
+  if (lch) return parseFloat(lch[1]) < 3;
+
+  // oklab()/lab() — a and b are signed axes; chroma = sqrt(a² + b²).
+  // oklab a/b are ~-0.4..0.4, threshold 0.02. lab a/b are ~-128..127, threshold 3.
+  const oklab = color.match(/oklab\(\s*[\d.%-]+\s+([\d.-]+)\s+([\d.-]+)/i);
+  if (oklab) {
+    const a = parseFloat(oklab[1]), b = parseFloat(oklab[2]);
+    return Math.hypot(a, b) < 0.02;
+  }
+  const lab = color.match(/lab\(\s*[\d.%-]+\s+([\d.-]+)\s+([\d.-]+)/i);
+  if (lab) {
+    const a = parseFloat(lab[1]), b = parseFloat(lab[2]);
+    return Math.hypot(a, b) < 3;
+  }
+
+  // hsl/hsla — saturation is the second numeric component (percent).
+  // Modern jsdom usually converts hsl() to rgb, but handle it directly for
+  // safety across versions and for any engine that preserves the format.
+  const hsl = color.match(/hsla?\(\s*[\d.-]+\s*,?\s*([\d.]+)%/i);
+  if (hsl) return parseFloat(hsl[1]) < 10;
+
+  // hwb(hue whiteness% blackness%) — a pixel is fully gray when
+  // whiteness + blackness >= 100; chroma-like saturation = 1 - (w+b)/100.
+  const hwb = color.match(/hwb\(\s*[\d.-]+\s+([\d.]+)%\s+([\d.]+)%/i);
+  if (hwb) {
+    const w = parseFloat(hwb[1]), b = parseFloat(hwb[2]);
+    return (1 - Math.min(100, w + b) / 100) < 0.1;
+  }
+
+  // Unknown / unrecognized format — err on the side of DETECTING rather
+  // than silently skipping. This is the opposite of the previous default,
+  // which was the root cause of the oklch bug.
+  return false;
+}
+
+function parseRgb(color) {
+  if (!color || color === 'transparent') return null;
+  const m = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
+  if (!m) return null;
+  return { r: +m[1], g: +m[2], b: +m[3], a: m[4] !== undefined ? +m[4] : 1 };
+}
+
+function relativeLuminance({ r, g, b }) {
+  const [rs, gs, bs] = [r / 255, g / 255, b / 255].map(c =>
+    c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4
+  );
+  return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
+}
+
+function contrastRatio(c1, c2) {
+  const l1 = relativeLuminance(c1);
+  const l2 = relativeLuminance(c2);
+  return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
+}
+
+function parseGradientColors(bgImage) {
+  if (!bgImage || !bgImage.includes('gradient')) return [];
+  const colors = [];
+  for (const m of bgImage.matchAll(/rgba?\([^)]+\)/g)) {
+    const c = parseRgb(m[0]);
+    if (c) colors.push(c);
+  }
+  for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) {
+    const h = m[1];
+    if (h.length === 6) {
+      colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 });
+    } else {
+      colors.push({ r: parseInt(h[0]+h[0],16), g: parseInt(h[1]+h[1],16), b: parseInt(h[2]+h[2],16), a: 1 });
+    }
+  }
+  return colors;
+}
+
+function hasChroma(c, threshold = 30) {
+  if (!c) return false;
+  return (Math.max(c.r, c.g, c.b) - Math.min(c.r, c.g, c.b)) >= threshold;
+}
+
+function getHue(c) {
+  if (!c) return 0;
+  const r = c.r / 255, g = c.g / 255, b = c.b / 255;
+  const max = Math.max(r, g, b), min = Math.min(r, g, b);
+  if (max === min) return 0;
+  const d = max - min;
+  let h;
+  if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
+  else if (max === g) h = ((b - r) / d + 2) / 6;
+  else h = ((r - g) / d + 4) / 6;
+  return Math.round(h * 360);
+}
+
+function colorToHex(c) {
+  if (!c) return '?';
+  return '#' + [c.r, c.g, c.b].map(v => v.toString(16).padStart(2, '0')).join('');
+}
+
+// ─── Section 3: Pure Detection ──────────────────────────────────────────────
+
+function checkBorders(tag, widths, colors, radius) {
+  if (BORDER_SAFE_TAGS.has(tag)) return [];
+  const findings = [];
+  const sides = ['Top', 'Right', 'Bottom', 'Left'];
+
+  for (const side of sides) {
+    const w = widths[side];
+    if (w < 1 || isNeutralColor(colors[side])) continue;
+
+    const otherSides = sides.filter(s => s !== side);
+    const maxOther = Math.max(...otherSides.map(s => widths[s]));
+    if (!(w >= 2 && (maxOther <= 1 || w >= maxOther * 2))) continue;
+
+    const sn = side.toLowerCase();
+    const isSide = side === 'Left' || side === 'Right';
+
+    if (isSide) {
+      if (radius > 0) findings.push({ id: 'side-tab', snippet: `border-${sn}: ${w}px + border-radius: ${radius}px` });
+      else if (w >= 3) findings.push({ id: 'side-tab', snippet: `border-${sn}: ${w}px` });
+    } else {
+      if (radius > 0 && w >= 2) findings.push({ id: 'border-accent-on-rounded', snippet: `border-${sn}: ${w}px + border-radius: ${radius}px` });
+    }
+  }
+
+  return findings;
+}
+
+// Returns true if the given text is composed entirely of emoji characters
+// (plus whitespace / variation selectors). Emojis render as multicolor glyphs
+// regardless of CSS `color`, so contrast checks against the element's text
+// color are meaningless for these nodes.
+const EMOJI_CHAR_RE = /[\u{1F1E6}-\u{1F1FF}\u{1F300}-\u{1F9FF}\u{1FA00}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}\u{FE0F}\u{200D}\u{1F3FB}-\u{1F3FF}]/u;
+const EMOJI_CHARS_GLOBAL = /[\u{1F1E6}-\u{1F1FF}\u{1F300}-\u{1F9FF}\u{1FA00}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}\u{FE0F}\u{200D}\u{1F3FB}-\u{1F3FF}]/gu;
+function isEmojiOnlyText(text) {
+  if (!text) return false;
+  if (!EMOJI_CHAR_RE.test(text)) return false;
+  return text.replace(EMOJI_CHARS_GLOBAL, '').trim() === '';
+}
+
+function checkColors(opts) {
+  const { tag, textColor, bgColor, effectiveBg, effectiveBgStops, fontSize, fontWeight, hasDirectText, isEmojiOnly, bgClip, bgImage, classList } = opts;
+  if (SAFE_TAGS.has(tag)) return [];
+  const findings = [];
+
+  // Pure black background (only solid or near-solid, not semi-transparent overlays)
+  if (bgColor && bgColor.a >= 0.9 && bgColor.r === 0 && bgColor.g === 0 && bgColor.b === 0) {
+    findings.push({ id: 'pure-black-white', snippet: '#000000 background' });
+  }
+
+  if (hasDirectText && textColor && !isEmojiOnly) {
+    // Run background-dependent checks against either a solid bg or, if the
+    // ancestor is a gradient, against every gradient stop (use the worst case).
+    const bgs = effectiveBg ? [effectiveBg] : (effectiveBgStops && effectiveBgStops.length ? effectiveBgStops : null);
+    if (bgs) {
+      // Gray on colored background — flag if every stop is chromatic
+      const textLum = relativeLuminance(textColor);
+      const isGray = !hasChroma(textColor, 20) && textLum > 0.05 && textLum < 0.85;
+      if (isGray && bgs.every(b => hasChroma(b, 40))) {
+        const bgLabel = effectiveBg ? colorToHex(effectiveBg) : `gradient(${bgs.map(colorToHex).join(', ')})`;
+        findings.push({ id: 'gray-on-color', snippet: `text ${colorToHex(textColor)} on bg ${bgLabel}` });
+      }
+
+      // Low contrast (WCAG AA) — worst case across all bg stops
+      const ratios = bgs.map(b => contrastRatio(textColor, b));
+      let worstIdx = 0;
+      for (let i = 1; i < ratios.length; i++) if (ratios[i] < ratios[worstIdx]) worstIdx = i;
+      const ratio = ratios[worstIdx];
+      const isHeading = ['h1', 'h2', 'h3'].includes(tag);
+      const isLargeText = fontSize >= 18 || (fontSize >= 14 && fontWeight >= 700) || isHeading;
+      const threshold = isLargeText ? 3.0 : 4.5;
+      if (ratio < threshold) {
+        findings.push({ id: 'low-contrast', snippet: `${ratio.toFixed(1)}:1 (need ${threshold}:1) — text ${colorToHex(textColor)} on ${colorToHex(bgs[worstIdx])}` });
+      }
+    }
+
+    // AI palette: purple/violet on headings
+    if (hasChroma(textColor, 50)) {
+      const hue = getHue(textColor);
+      if (hue >= 260 && hue <= 310 && (['h1', 'h2', 'h3'].includes(tag) || fontSize >= 20)) {
+        findings.push({ id: 'ai-color-palette', snippet: `Purple/violet text (${colorToHex(textColor)}) on heading` });
+      }
+    }
+  }
+
+  // Gradient text
+  if (bgClip === 'text' && bgImage && bgImage.includes('gradient')) {
+    findings.push({ id: 'gradient-text', snippet: 'background-clip: text + gradient' });
+  }
+
+  // Tailwind class checks
+  if (classList) {
+    const classStr = typeof classList === 'string' ? classList : Array.from(classList).join(' ');
+    if (/\bbg-black\b(?!\/)/.test(classStr)) {
+      findings.push({ id: 'pure-black-white', snippet: 'bg-black' });
+    }
+
+    const grayMatch = classStr.match(/\btext-(?:gray|slate|zinc|neutral|stone)-\d+\b/);
+    const colorBgMatch = classStr.match(/\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/);
+    if (grayMatch && colorBgMatch) {
+      findings.push({ id: 'gray-on-color', snippet: `${grayMatch[0]} on ${colorBgMatch[0]}` });
+    }
+
+    if (/\bbg-clip-text\b/.test(classStr) && /\bbg-gradient-to-/.test(classStr)) {
+      findings.push({ id: 'gradient-text', snippet: 'bg-clip-text + bg-gradient (Tailwind)' });
+    }
+
+    const purpleText = classStr.match(/\btext-(?:purple|violet|indigo)-\d+\b/);
+    if (purpleText && (['h1', 'h2', 'h3'].includes(tag) || /\btext-(?:[2-9]xl)\b/.test(classStr))) {
+      findings.push({ id: 'ai-color-palette', snippet: `${purpleText[0]} on heading` });
+    }
+
+    if (/\bfrom-(?:purple|violet|indigo)-\d+\b/.test(classStr) && /\bto-(?:purple|violet|indigo|blue|cyan|pink|fuchsia)-\d+\b/.test(classStr)) {
+      findings.push({ id: 'ai-color-palette', snippet: 'Purple/violet gradient (Tailwind)' });
+    }
+  }
+
+  return findings;
+}
+
+function isCardLikeFromProps(hasShadow, hasBorder, hasRadius, hasBg) {
+  if (!hasShadow && !hasBorder) return false;
+  return hasRadius || hasBg;
+}
+
+const HEADING_TAGS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']);
+
+// Pure check: given a heading and metrics about its previousElementSibling,
+// decide if the sibling is the canonical "icon-tile-stacked-above-heading" shape.
+//
+// Triggers when ALL of the following hold for the sibling:
+//   • size 32–128px on both axes (not too small, not a hero image)
+//   • aspect ratio 0.7–1.4 (squarish — excludes wide thumbnails / pill badges)
+//   • has a non-transparent background-color, background-image, OR a visible border
+//     (covers solid colors, white-with-border, gradients — anything that visually
+//      defines a tile)
+//   • border-radius < width/2 (excludes round avatars; rounded squares pass)
+//   • contains an <svg> or icon-class <i> element that's smaller than the tile
+//   • the tile sits above the heading (its bottom is above the heading's top)
+function checkIconTile(opts) {
+  const { headingTag, headingText, headingTop,
+          siblingTag, siblingWidth, siblingHeight, siblingBottom,
+          siblingBgColor, siblingBgImage, siblingBorderWidth, siblingBorderRadius,
+          hasIconChild, iconChildWidth } = opts;
+  if (!HEADING_TAGS.has(headingTag)) return [];
+  if (!siblingTag) return [];
+  // Don't recurse into nested headings (e.g. h2 above h3 in a section header)
+  if (HEADING_TAGS.has(siblingTag)) return [];
+
+  // Size window: 32–128px on each axis
+  if (!(siblingWidth >= 32 && siblingWidth <= 128)) return [];
+  if (!(siblingHeight >= 32 && siblingHeight <= 128)) return [];
+
+  // Squarish aspect ratio
+  const ratio = siblingWidth / siblingHeight;
+  if (ratio < 0.7 || ratio > 1.4) return [];
+
+  // Must have something that visually defines the tile
+  const bgVisible = (siblingBgColor && siblingBgColor.a > 0.1)
+    || (siblingBgImage && siblingBgImage !== 'none' && siblingBgImage !== '');
+  const borderVisible = siblingBorderWidth > 0;
+  if (!bgVisible && !borderVisible) return [];
+
+  // Exclude circles (avatars). Rounded squares pass.
+  if (siblingBorderRadius >= siblingWidth / 2) return [];
+
+  // Must contain an icon element smaller than the tile
+  if (!hasIconChild) return [];
+  if (iconChildWidth && iconChildWidth >= siblingWidth * 0.95) return [];
+
+  // Vertical stacking: tile must end above where the heading starts.
+  // (Allow the check to skip when both top/bottom are 0 — jsdom layout case.)
+  if (headingTop && siblingBottom && siblingBottom > headingTop + 4) return [];
+
+  const text = (headingText || '').trim().slice(0, 60);
+  return [{
+    id: 'icon-tile-stack',
+    snippet: `${Math.round(siblingWidth)}x${Math.round(siblingHeight)}px icon tile above ${headingTag} "${text}"`,
+  }];
+}
+
+const LAYOUT_TRANSITION_PROPS = new Set([
+  'width', 'height', 'padding', 'margin',
+  'max-height', 'max-width', 'min-height', 'min-width',
+  'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
+  'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
+]);
+
+function checkMotion(opts) {
+  const { tag, transitionProperty, animationName, timingFunctions, classList } = opts;
+  if (SAFE_TAGS.has(tag)) return [];
+  const findings = [];
+
+  // --- Bounce/elastic easing ---
+  if (animationName && animationName !== 'none' && /bounce|elastic|wobble|jiggle|spring/i.test(animationName)) {
+    findings.push({ id: 'bounce-easing', snippet: `animation: ${animationName}` });
+  }
+  if (classList && /\banimate-bounce\b/.test(classList)) {
+    findings.push({ id: 'bounce-easing', snippet: 'animate-bounce (Tailwind)' });
+  }
+
+  // Check timing functions for overshoot cubic-bezier (y values outside [0, 1])
+  if (timingFunctions) {
+    const bezierRe = /cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)/g;
+    let m;
+    while ((m = bezierRe.exec(timingFunctions)) !== null) {
+      const y1 = parseFloat(m[2]), y2 = parseFloat(m[4]);
+      if (y1 < -0.1 || y1 > 1.1 || y2 < -0.1 || y2 > 1.1) {
+        findings.push({ id: 'bounce-easing', snippet: `cubic-bezier(${m[1]}, ${m[2]}, ${m[3]}, ${m[4]})` });
+        break;
+      }
+    }
+  }
+
+  // --- Layout property transition ---
+  if (transitionProperty && transitionProperty !== 'all' && transitionProperty !== 'none') {
+    const props = transitionProperty.split(',').map(p => p.trim().toLowerCase());
+    const layoutFound = props.filter(p => LAYOUT_TRANSITION_PROPS.has(p));
+    if (layoutFound.length > 0) {
+      findings.push({ id: 'layout-transition', snippet: `transition: ${layoutFound.join(', ')}` });
+    }
+  }
+
+  return findings;
+}
+
+function checkGlow(opts) {
+  const { boxShadow, effectiveBg } = opts;
+  if (!boxShadow || boxShadow === 'none') return [];
+  if (!effectiveBg) return [];
+
+  // Only flag on dark backgrounds (luminance < 0.1)
+  const bgLum = relativeLuminance(effectiveBg);
+  if (bgLum >= 0.1) return [];
+
+  // Split multiple shadows (commas not inside parentheses)
+  const parts = boxShadow.split(/,(?![^(]*\))/);
+  for (const shadow of parts) {
+    const colorMatch = shadow.match(/rgba?\([^)]+\)/);
+    if (!colorMatch) continue;
+    const color = parseRgb(colorMatch[0]);
+    if (!color || !hasChroma(color, 30)) continue;
+
+    // Extract px values — in computed style: "color Xpx Ypx BLURpx [SPREADpx]"
+    const afterColor = shadow.substring(shadow.indexOf(colorMatch[0]) + colorMatch[0].length);
+    const beforeColor = shadow.substring(0, shadow.indexOf(colorMatch[0]));
+    const pxVals = [...beforeColor.matchAll(/([\d.]+)px/g), ...afterColor.matchAll(/([\d.]+)px/g)]
+      .map(m => parseFloat(m[1]));
+
+    // Third value is blur (offset-x, offset-y, blur, [spread])
+    if (pxVals.length >= 3 && pxVals[2] > 4) {
+      return [{ id: 'dark-glow', snippet: `Colored glow (${colorToHex(color)}) on dark background` }];
+    }
+  }
+
+  return [];
+}
+
+/**
+ * Regex-on-HTML checks shared between browser and Node page-level detection.
+ * These don't need DOM access, just the raw HTML string.
+ */
+function checkHtmlPatterns(html) {
+  const findings = [];
+
+  // --- Color ---
+
+  // Pure black background
+  const pureBlackBgRe = /background(?:-color)?\s*:\s*(?:#000000|#000|rgb\(\s*0,\s*0,\s*0\s*\))\b/gi;
+  if (pureBlackBgRe.test(html)) {
+    findings.push({ id: 'pure-black-white', snippet: 'Pure #000 background' });
+  }
+
+  // AI color palette: purple/violet
+  const purpleHexRe = /#(?:7c3aed|8b5cf6|a855f7|9333ea|7e22ce|6d28d9|6366f1|764ba2|667eea)\b/gi;
+  if (purpleHexRe.test(html)) {
+    const purpleTextRe = /(?:(?:^|;)\s*color\s*:\s*(?:.*?)(?:#(?:7c3aed|8b5cf6|a855f7|9333ea|7e22ce|6d28d9))|gradient.*?#(?:7c3aed|8b5cf6|a855f7|764ba2|667eea))/gi;
+    if (purpleTextRe.test(html)) {
+      findings.push({ id: 'ai-color-palette', snippet: 'Purple/violet accent colors detected' });
+    }
+  }
+
+  // Gradient text (background-clip: text + gradient)
+  const gradientRe = /(?:-webkit-)?background-clip\s*:\s*text/gi;
+  let gm;
+  while ((gm = gradientRe.exec(html)) !== null) {
+    const start = Math.max(0, gm.index - 200);
+    const context = html.substring(start, gm.index + gm[0].length + 200);
+    if (/gradient/i.test(context)) {
+      findings.push({ id: 'gradient-text', snippet: 'background-clip: text + gradient' });
+      break;
+    }
+  }
+  if (/\bbg-clip-text\b/.test(html) && /\bbg-gradient-to-/.test(html)) {
+    findings.push({ id: 'gradient-text', snippet: 'bg-clip-text + bg-gradient (Tailwind)' });
+  }
+
+  // --- Layout ---
+
+  // Monotonous spacing
+  const spacingValues = [];
+  const spacingRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*(\d+)px/gi;
+  let sm;
+  while ((sm = spacingRe.exec(html)) !== null) {
+    const v = parseInt(sm[1], 10);
+    if (v > 0 && v < 200) spacingValues.push(v);
+  }
+  const gapRe = /gap\s*:\s*(\d+)px/gi;
+  while ((sm = gapRe.exec(html)) !== null) {
+    spacingValues.push(parseInt(sm[1], 10));
+  }
+  const twSpaceRe = /\b(?:p|px|py|pt|pb|pl|pr|m|mx|my|mt|mb|ml|mr|gap)-(\d+)\b/g;
+  while ((sm = twSpaceRe.exec(html)) !== null) {
+    spacingValues.push(parseInt(sm[1], 10) * 4);
+  }
+  const remSpacingRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*([\d.]+)rem/gi;
+  while ((sm = remSpacingRe.exec(html)) !== null) {
+    const v = Math.round(parseFloat(sm[1]) * 16);
+    if (v > 0 && v < 200) spacingValues.push(v);
+  }
+  const roundedSpacing = spacingValues.map(v => Math.round(v / 4) * 4);
+  if (roundedSpacing.length >= 10) {
+    const counts = {};
+    for (const v of roundedSpacing) counts[v] = (counts[v] || 0) + 1;
+    const maxCount = Math.max(...Object.values(counts));
+    const dominantPct = maxCount / roundedSpacing.length;
+    const unique = [...new Set(roundedSpacing)].filter(v => v > 0);
+    if (dominantPct > 0.6 && unique.length <= 3) {
+      const dominant = Object.entries(counts).sort((a, b) => b[1] - a[1])[0][0];
+      findings.push({
+        id: 'monotonous-spacing',
+        snippet: `~${dominant}px used ${maxCount}/${roundedSpacing.length} times (${Math.round(dominantPct * 100)}%)`,
+      });
+    }
+  }
+
+  // --- Motion ---
+
+  // Bounce/elastic animation names
+  const bounceRe = /animation(?:-name)?\s*:\s*[^;]*\b(bounce|elastic|wobble|jiggle|spring)\b/gi;
+  if (bounceRe.test(html)) {
+    findings.push({ id: 'bounce-easing', snippet: 'Bounce/elastic animation in CSS' });
+  }
+
+  // Overshoot cubic-bezier
+  const bezierRe = /cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)/g;
+  let bm;
+  while ((bm = bezierRe.exec(html)) !== null) {
+    const y1 = parseFloat(bm[2]), y2 = parseFloat(bm[4]);
+    if (y1 < -0.1 || y1 > 1.1 || y2 < -0.1 || y2 > 1.1) {
+      findings.push({ id: 'bounce-easing', snippet: `cubic-bezier(${bm[1]}, ${bm[2]}, ${bm[3]}, ${bm[4]})` });
+      break;
+    }
+  }
+
+  // Layout property transitions
+  const transRe = /transition(?:-property)?\s*:\s*([^;{}]+)/gi;
+  let tm;
+  while ((tm = transRe.exec(html)) !== null) {
+    const val = tm[1].toLowerCase();
+    if (/\ball\b/.test(val)) continue;
+    const found = val.match(/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi);
+    if (found) {
+      findings.push({ id: 'layout-transition', snippet: `transition: ${found.join(', ')}` });
+      break;
+    }
+  }
+
+  // --- Dark glow ---
+
+  const darkBgRe = /background(?:-color)?\s*:\s*(?:#(?:0[0-9a-f]|1[0-9a-f]|2[0-3])[0-9a-f]{4}\b|#(?:0|1)[0-9a-f]{2}\b|rgb\(\s*(\d{1,2})\s*,\s*(\d{1,2})\s*,\s*(\d{1,2})\s*\))/gi;
+  const twDarkBg = /\bbg-(?:gray|slate|zinc|neutral|stone)-(?:9\d{2}|800)\b/;
+  if (darkBgRe.test(html) || twDarkBg.test(html)) {
+    const shadowRe = /box-shadow\s*:\s*([^;{}]+)/gi;
+    let shm;
+    while ((shm = shadowRe.exec(html)) !== null) {
+      const val = shm[1];
+      const colorMatch = val.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
+      if (!colorMatch) continue;
+      const [r, g, b] = [+colorMatch[1], +colorMatch[2], +colorMatch[3]];
+      if ((Math.max(r, g, b) - Math.min(r, g, b)) < 30) continue;
+      const pxVals = [...val.matchAll(/(\d+)px|(?<![.\d])\b(0)\b(?![.\d])/g)].map(p => +(p[1] || p[2]));
+      if (pxVals.length >= 3 && pxVals[2] > 4) {
+        findings.push({ id: 'dark-glow', snippet: `Colored glow (rgb(${r},${g},${b})) on dark page` });
+        break;
+      }
+    }
+  }
+
+  return findings;
+}
+
+// ─── Section 4: resolveBackground (unified) ─────────────────────────────────
+
+function resolveBackground(el, win) {
+  let current = el;
+  while (current && current.nodeType === 1) {
+    const style = IS_BROWSER ? getComputedStyle(current) : win.getComputedStyle(current);
+
+    // If this element has a background-image (gradient or url), it's visually
+    // opaque but we can't determine the effective color — bail out so callers
+    // don't get a false solid-color answer.
+    const bgImage = style.backgroundImage || '';
+    if (bgImage && bgImage !== 'none' && (/gradient/i.test(bgImage) || /url\s*\(/i.test(bgImage))) {
+      return null;
+    }
+
+    let bg = parseRgb(style.backgroundColor);
+    if (!IS_BROWSER && (!bg || bg.a < 0.1)) {
+      // jsdom doesn't decompose background shorthand — parse raw style attr
+      const rawStyle = current.getAttribute?.('style') || '';
+      const bgMatch = rawStyle.match(/background(?:-color)?\s*:\s*([^;]+)/i);
+      const inlineBg = bgMatch ? bgMatch[1].trim() : '';
+      // Check for gradient or url() image in inline style too
+      if (/gradient/i.test(inlineBg) || /url\s*\(/i.test(inlineBg)) return null;
+      bg = parseRgb(inlineBg);
+      if (!bg && inlineBg) {
+        const hexMatch = inlineBg.match(/#([0-9a-f]{6}|[0-9a-f]{3})\b/i);
+        if (hexMatch) {
+          const h = hexMatch[1];
+          if (h.length === 6) {
+            bg = { r: parseInt(h.slice(0,2), 16), g: parseInt(h.slice(2,4), 16), b: parseInt(h.slice(4,6), 16), a: 1 };
+          } else {
+            bg = { r: parseInt(h[0]+h[0], 16), g: parseInt(h[1]+h[1], 16), b: parseInt(h[2]+h[2], 16), a: 1 };
+          }
+        }
+      }
+    }
+    if (bg && bg.a > 0.1) {
+      if (IS_BROWSER || bg.a >= 0.5) return bg;
+    }
+    current = current.parentElement;
+  }
+  return { r: 255, g: 255, b: 255 };
+}
+
+// Walk parents looking for a gradient background and return its color stops.
+// Used as a fallback when resolveBackground() returns null because the
+// effective background is a gradient (no single solid color to compare against).
+function resolveGradientStops(el, win) {
+  let current = el;
+  while (current && current.nodeType === 1) {
+    const style = IS_BROWSER ? getComputedStyle(current) : win.getComputedStyle(current);
+    const bgImage = style.backgroundImage || '';
+    if (bgImage && bgImage !== 'none' && /gradient/i.test(bgImage)) {
+      const stops = parseGradientColors(bgImage);
+      if (stops.length > 0) return stops;
+    }
+    if (!IS_BROWSER) {
+      // jsdom doesn't decompose `background:` shorthand — peek at the raw inline style
+      const rawStyle = current.getAttribute?.('style') || '';
+      const bgMatch = rawStyle.match(/background(?:-image)?\s*:\s*([^;]+)/i);
+      if (bgMatch && /gradient/i.test(bgMatch[1])) {
+        const stops = parseGradientColors(bgMatch[1]);
+        if (stops.length > 0) return stops;
+      }
+    }
+    current = current.parentElement;
+  }
+  return null;
+}
+
+// ─── Section 5: Element Adapters ────────────────────────────────────────────
+
+// Browser adapters — call getComputedStyle/getBoundingClientRect on live DOM
+
+function checkElementBordersDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (BORDER_SAFE_TAGS.has(tag)) return [];
+  const rect = el.getBoundingClientRect();
+  if (rect.width < 20 || rect.height < 20) return [];
+  const style = getComputedStyle(el);
+  const sides = ['Top', 'Right', 'Bottom', 'Left'];
+  const widths = {}, colors = {};
+  for (const s of sides) {
+    widths[s] = parseFloat(style[`border${s}Width`]) || 0;
+    colors[s] = style[`border${s}Color`] || '';
+  }
+  return checkBorders(tag, widths, colors, parseFloat(style.borderRadius) || 0);
+}
+
+function checkElementColorsDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (SAFE_TAGS.has(tag)) return [];
+  const rect = el.getBoundingClientRect();
+  if (rect.width < 10 || rect.height < 10) return [];
+  const style = getComputedStyle(el);
+  const directText = [...el.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
+  const hasDirectText = directText.trim().length > 0;
+  const effectiveBg = resolveBackground(el);
+  return checkColors({
+    tag,
+    textColor: parseRgb(style.color),
+    bgColor: parseRgb(style.backgroundColor),
+    effectiveBg,
+    effectiveBgStops: effectiveBg ? null : resolveGradientStops(el),
+    fontSize: parseFloat(style.fontSize) || 16,
+    fontWeight: parseInt(style.fontWeight) || 400,
+    hasDirectText,
+    isEmojiOnly: isEmojiOnlyText(directText),
+    bgClip: style.webkitBackgroundClip || style.backgroundClip || '',
+    bgImage: style.backgroundImage || '',
+    classList: el.getAttribute('class') || '',
+  });
+}
+
+function checkElementIconTileDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (!HEADING_TAGS.has(tag)) return [];
+  const sibling = el.previousElementSibling;
+  if (!sibling) return [];
+
+  const sibRect = sibling.getBoundingClientRect();
+  const headRect = el.getBoundingClientRect();
+  const sibStyle = getComputedStyle(sibling);
+
+  // The tile may either contain an <svg>/<i> icon child, OR the tile itself
+  // may contain an emoji/symbol character directly as its only text content
+  // (the "card-icon" pattern from many AI-generated demos).
+  const iconChild = sibling.querySelector('svg, i[data-lucide], i[class*="fa-"], i[class*="icon"]');
+  const iconRect = iconChild?.getBoundingClientRect();
+  const sibDirectText = [...sibling.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
+  const hasInlineEmojiIcon = sibling.children.length === 0 && isEmojiOnlyText(sibDirectText);
+
+  return checkIconTile({
+    headingTag: tag,
+    headingText: el.textContent || '',
+    headingTop: headRect.top,
+    siblingTag: sibling.tagName.toLowerCase(),
+    siblingWidth: sibRect.width,
+    siblingHeight: sibRect.height,
+    siblingBottom: sibRect.bottom,
+    siblingBgColor: parseRgb(sibStyle.backgroundColor),
+    siblingBgImage: sibStyle.backgroundImage || '',
+    siblingBorderWidth: parseFloat(sibStyle.borderTopWidth) || 0,
+    siblingBorderRadius: parseFloat(sibStyle.borderRadius) || 0,
+    hasIconChild: !!iconChild || hasInlineEmojiIcon,
+    iconChildWidth: iconRect?.width || 0,
+  });
+}
+
+function checkElementMotionDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (SAFE_TAGS.has(tag)) return [];
+  const style = getComputedStyle(el);
+  return checkMotion({
+    tag,
+    transitionProperty: style.transitionProperty || '',
+    animationName: style.animationName || '',
+    timingFunctions: [style.animationTimingFunction, style.transitionTimingFunction].filter(Boolean).join(' '),
+    classList: el.getAttribute('class') || '',
+  });
+}
+
+function checkElementGlowDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  const style = getComputedStyle(el);
+  if (!style.boxShadow || style.boxShadow === 'none') return [];
+  // Use parent's background — glow radiates outward, so the surrounding context matters
+  // If resolveBackground returns null (gradient), try to infer from the gradient colors
+  let parentBg = el.parentElement ? resolveBackground(el.parentElement) : resolveBackground(el);
+  if (!parentBg) {
+    // Gradient background — sample its colors to determine if it's dark
+    let cur = el.parentElement;
+    while (cur && cur.nodeType === 1) {
+      const bgImage = getComputedStyle(cur).backgroundImage || '';
+      const gradColors = parseGradientColors(bgImage);
+      if (gradColors.length > 0) {
+        // Average the gradient colors
+        const avg = { r: 0, g: 0, b: 0 };
+        for (const c of gradColors) { avg.r += c.r; avg.g += c.g; avg.b += c.b; }
+        avg.r = Math.round(avg.r / gradColors.length);
+        avg.g = Math.round(avg.g / gradColors.length);
+        avg.b = Math.round(avg.b / gradColors.length);
+        parentBg = avg;
+        break;
+      }
+      cur = cur.parentElement;
+    }
+  }
+  return checkGlow({ tag, boxShadow: style.boxShadow, effectiveBg: parentBg });
+}
+
+function checkElementAIPaletteDOM(el) {
+  const style = getComputedStyle(el);
+  const findings = [];
+
+  // Check gradient backgrounds for purple/violet or cyan
+  const bgImage = style.backgroundImage || '';
+  const gradColors = parseGradientColors(bgImage);
+  for (const c of gradColors) {
+    if (hasChroma(c, 50)) {
+      const hue = getHue(c);
+      if (hue >= 260 && hue <= 310) {
+        findings.push({ id: 'ai-color-palette', snippet: 'Purple/violet gradient background' });
+        break;
+      }
+      if (hue >= 160 && hue <= 200) {
+        findings.push({ id: 'ai-color-palette', snippet: 'Cyan gradient background' });
+        break;
+      }
+    }
+  }
+
+  // Check for neon text (vivid cyan/purple color on dark background)
+  const textColor = parseRgb(style.color);
+  if (textColor && hasChroma(textColor, 80)) {
+    const hue = getHue(textColor);
+    const isAIPalette = (hue >= 160 && hue <= 200) || (hue >= 260 && hue <= 310);
+    if (isAIPalette) {
+      const parentBg = el.parentElement ? resolveBackground(el.parentElement) : null;
+      // Also check gradient parents
+      let effectiveBg = parentBg;
+      if (!effectiveBg) {
+        let cur = el.parentElement;
+        while (cur && cur.nodeType === 1) {
+          const gi = getComputedStyle(cur).backgroundImage || '';
+          const gc = parseGradientColors(gi);
+          if (gc.length > 0) {
+            const avg = { r: 0, g: 0, b: 0 };
+            for (const c of gc) { avg.r += c.r; avg.g += c.g; avg.b += c.b; }
+            avg.r = Math.round(avg.r / gc.length);
+            avg.g = Math.round(avg.g / gc.length);
+            avg.b = Math.round(avg.b / gc.length);
+            effectiveBg = avg;
+            break;
+          }
+          cur = cur.parentElement;
+        }
+      }
+      if (effectiveBg && relativeLuminance(effectiveBg) < 0.1) {
+        const label = hue >= 260 ? 'Purple/violet' : 'Cyan';
+        findings.push({ id: 'ai-color-palette', snippet: `${label} neon text on dark background` });
+      }
+    }
+  }
+
+  return findings;
+}
+
+const QUALITY_TEXT_TAGS = new Set(['p', 'li', 'td', 'th', 'dd', 'blockquote', 'figcaption']);
+
+// Resolve a CSS font-size value to pixels by walking up the parent chain.
+// Browsers resolve em/rem/% to px in getComputedStyle, but jsdom returns the
+// specified value verbatim — so for the Node path we walk parents ourselves.
+function resolveFontSizePx(el, win) {
+  const chain = []; // raw font-size strings, leaf → root
+  let cur = el;
+  while (cur && cur.nodeType === 1) {
+    const fs = (win ? win.getComputedStyle(cur) : getComputedStyle(cur)).fontSize;
+    chain.push(fs || '');
+    cur = cur.parentElement;
+  }
+  // Walk root → leaf, resolving each value relative to its parent context.
+  let px = 16; // root default
+  for (let i = chain.length - 1; i >= 0; i--) {
+    const v = chain[i];
+    if (!v || v === 'inherit') continue;
+    const num = parseFloat(v);
+    if (isNaN(num)) continue;
+    if (v.endsWith('px')) px = num;
+    else if (v.endsWith('rem')) px = num * 16;
+    else if (v.endsWith('em')) px = num * px;
+    else if (v.endsWith('%')) px = (num / 100) * px;
+    else px = num; // unitless — already resolved
+  }
+  return px;
+}
+
+// Resolve a CSS length value (line-height, letter-spacing, etc.) given a
+// known font-size context. Returns null for "normal" / unparseable values.
+function resolveLengthPx(value, fontSizePx) {
+  if (!value || value === 'normal' || value === 'auto' || value === 'inherit') return null;
+  const num = parseFloat(value);
+  if (isNaN(num)) return null;
+  if (value.endsWith('px')) return num;
+  if (value.endsWith('rem')) return num * 16;
+  if (value.endsWith('em')) return num * fontSizePx;
+  if (value.endsWith('%')) return (num / 100) * fontSizePx;
+  // Unitless line-height = multiplier, return px equivalent
+  return num * fontSizePx;
+}
+
+// Pure quality checks. Most run on computed CSS and DOM-only inputs (work in
+// jsdom and the browser). Two checks (line-length, cramped-padding) gate on
+// element rect dimensions, which jsdom can't compute — pass `rect: null` from
+// the Node adapter to skip those.
+//
+// Both adapters resolve font-size, line-height and letter-spacing to pixels
+// before calling this so the pure function only deals with numbers.
+function checkQuality(opts) {
+  const { el, tag, style, hasDirectText, textLen, fontSize, lineHeightPx, letterSpacingPx, rect, lineMax = 80 } = opts;
+  const findings = [];
+  // Skip browser extension injected elements
+  const elId = el.id || '';
+  if (elId.startsWith('claude-') || elId.startsWith('cic-')) return findings;
+
+  // --- Line length too long --- (browser-only: needs rect.width)
+  if (rect && hasDirectText && QUALITY_TEXT_TAGS.has(tag) && rect.width > 0 && textLen > lineMax) {
+    const charsPerLine = rect.width / (fontSize * 0.5);
+    if (charsPerLine > lineMax + 5) {
+      findings.push({ id: 'line-length', snippet: `~${Math.round(charsPerLine)} chars/line (aim for <${lineMax})` });
+    }
+  }
+
+  // --- Cramped padding --- (browser-only: needs rect to skip small badges/labels)
+  // Vertical and horizontal thresholds are independent because line-height
+  // already provides built-in vertical breathing room (the line box is taller
+  // than the cap height), but horizontal has no equivalent. Both scale with
+  // font-size — bigger text demands proportionally more padding.
+  //   vertical:   max(4px, fontSize × 0.3)
+  //   horizontal: max(8px, fontSize × 0.5)
+  if (rect && hasDirectText && textLen > 20 && rect.width > 100 && rect.height > 30) {
+    const borders = {
+      top: parseFloat(style.borderTopWidth) || 0,
+      right: parseFloat(style.borderRightWidth) || 0,
+      bottom: parseFloat(style.borderBottomWidth) || 0,
+      left: parseFloat(style.borderLeftWidth) || 0,
+    };
+    const borderCount = Object.values(borders).filter(w => w > 0).length;
+    const hasBg = style.backgroundColor && style.backgroundColor !== 'rgba(0, 0, 0, 0)';
+    if (borderCount >= 2 || hasBg) {
+      const vPads = [], hPads = [];
+      if (hasBg || borders.top > 0) vPads.push(parseFloat(style.paddingTop) || 0);
+      if (hasBg || borders.bottom > 0) vPads.push(parseFloat(style.paddingBottom) || 0);
+      if (hasBg || borders.left > 0) hPads.push(parseFloat(style.paddingLeft) || 0);
+      if (hasBg || borders.right > 0) hPads.push(parseFloat(style.paddingRight) || 0);
+
+      const vMin = vPads.length ? Math.min(...vPads) : Infinity;
+      const hMin = hPads.length ? Math.min(...hPads) : Infinity;
+      const vThresh = Math.max(4, fontSize * 0.3);
+      const hThresh = Math.max(8, fontSize * 0.5);
+
+      // Emit at most one finding per element — pick whichever axis is worse.
+      if (vMin < vThresh) {
+        findings.push({ id: 'cramped-padding', snippet: `${vMin}px vertical padding (need ≥${vThresh.toFixed(1)}px for ${fontSize}px text)` });
+      } else if (hMin < hThresh) {
+        findings.push({ id: 'cramped-padding', snippet: `${hMin}px horizontal padding (need ≥${hThresh.toFixed(1)}px for ${fontSize}px text)` });
+      }
+    }
+  }
+
+  // --- Tight line height ---
+  if (hasDirectText && textLen > 50 && !['h1','h2','h3','h4','h5','h6'].includes(tag)) {
+    if (lineHeightPx != null && fontSize > 0) {
+      const ratio = lineHeightPx / fontSize;
+      if (ratio > 0 && ratio < 1.3) {
+        findings.push({ id: 'tight-leading', snippet: `line-height ${ratio.toFixed(2)}x (need >=1.3)` });
+      }
+    }
+  }
+
+  // --- Justified text (without hyphens) ---
+  if (hasDirectText && style.textAlign === 'justify') {
+    const hyphens = style.hyphens || style.webkitHyphens || '';
+    if (hyphens !== 'auto') {
+      findings.push({ id: 'justified-text', snippet: 'text-align: justify without hyphens: auto' });
+    }
+  }
+
+  // --- Tiny body text ---
+  // Only flag actual body content, not UI labels (buttons, tabs, badges, captions, footer text, etc.)
+  if (hasDirectText && textLen > 20 && fontSize < 12) {
+    const skipTags = ['sub', 'sup', 'code', 'kbd', 'samp', 'var', 'caption', 'figcaption'];
+    const inUIContext = el.closest && el.closest('button, a, label, summary, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="option"], nav, footer, [class*="badge" i], [class*="chip" i], [class*="pill" i], [class*="tag" i], [class*="label" i], [class*="caption" i]');
+    const isUppercase = style.textTransform === 'uppercase';
+    if (!skipTags.includes(tag) && !inUIContext && !isUppercase) {
+      findings.push({ id: 'tiny-text', snippet: `${fontSize}px body text` });
+    }
+  }
+
+  // --- All-caps body text ---
+  if (hasDirectText && textLen > 30 && style.textTransform === 'uppercase') {
+    if (!['h1','h2','h3','h4','h5','h6'].includes(tag)) {
+      findings.push({ id: 'all-caps-body', snippet: `text-transform: uppercase on ${textLen} chars of body text` });
+    }
+  }
+
+  // --- Wide letter spacing on body text ---
+  if (hasDirectText && textLen > 20 && style.textTransform !== 'uppercase') {
+    if (letterSpacingPx != null && letterSpacingPx > 0 && fontSize > 0) {
+      const trackingEm = letterSpacingPx / fontSize;
+      if (trackingEm > 0.05) {
+        findings.push({ id: 'wide-tracking', snippet: `letter-spacing: ${trackingEm.toFixed(2)}em on body text` });
+      }
+    }
+  }
+
+  return findings;
+}
+
+function checkElementQualityDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  const style = getComputedStyle(el);
+  const hasDirectText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim().length > 10);
+  const textLen = el.textContent?.trim().length || 0;
+  // Browser getComputedStyle resolves everything to px — direct parseFloat
+  // works.
+  const fontSize = parseFloat(style.fontSize) || 16;
+  const lineHeightPx = resolveLengthPx(style.lineHeight, fontSize);
+  const letterSpacingPx = resolveLengthPx(style.letterSpacing, fontSize);
+  const rect = el.getBoundingClientRect();
+  const lineMax = (typeof window !== 'undefined' && window.__IMPECCABLE_CONFIG__?.lineLengthMax) || 80;
+  return checkQuality({ el, tag, style, hasDirectText, textLen, fontSize, lineHeightPx, letterSpacingPx, rect, lineMax });
+}
+
+// Pure page-level skipped-heading walk. Takes a Document so it works in both
+// the browser and jsdom.
+function checkPageQualityFromDoc(doc) {
+  const findings = [];
+  const headings = doc.querySelectorAll('h1, h2, h3, h4, h5, h6');
+  let prevLevel = 0;
+  let prevText = '';
+  for (const h of headings) {
+    const level = parseInt(h.tagName[1]);
+    const text = (h.textContent || '').trim().replace(/\s+/g, ' ').slice(0, 60);
+    if (prevLevel > 0 && level > prevLevel + 1) {
+      findings.push({
+        id: 'skipped-heading',
+        snippet: `<h${prevLevel}> "${prevText}" followed by <h${level}> "${text}" (missing h${prevLevel + 1})`,
+      });
+    }
+    prevLevel = level;
+    prevText = text;
+  }
+  return findings;
+}
+
+// Browser adapter (returns the legacy { type, detail } shape used by the overlay loop)
+function checkPageQualityDOM() {
+  return checkPageQualityFromDoc(document).map(f => ({ type: f.id, detail: f.snippet }));
+}
+
+// Node adapters — take pre-extracted jsdom computed style
+
+// jsdom doesn't lay out OR resolve em/rem/% to px — so we pre-resolve every
+// CSS length the rule needs ourselves (walking the parent chain for
+// font-size inheritance), and pass `rect: null` to skip the two rules that
+// genuinely need element rects (line-length, cramped-padding).
+function checkElementQuality(el, style, tag, window) {
+  const hasDirectText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim().length > 10);
+  const textLen = el.textContent?.trim().length || 0;
+  const fontSize = resolveFontSizePx(el, window);
+  const lineHeightPx = resolveLengthPx(style.lineHeight, fontSize);
+  const letterSpacingPx = resolveLengthPx(style.letterSpacing, fontSize);
+  return checkQuality({ el, tag, style, hasDirectText, textLen, fontSize, lineHeightPx, letterSpacingPx, rect: null });
+}
+
+function checkElementBorders(tag, style, overrides) {
+  const sides = ['Top', 'Right', 'Bottom', 'Left'];
+  const widths = {}, colors = {};
+  for (const s of sides) {
+    widths[s] = parseFloat(style[`border${s}Width`]) || 0;
+    colors[s] = style[`border${s}Color`] || '';
+    // jsdom silently drops any border shorthand containing var(), leaving
+    // both width and color empty on the computed style. When the detectHtml
+    // pre-pass pulled a resolved value off the rule, use it to fill in the
+    // missing side so the side-tab check can run. Real browsers resolve
+    // var() natively, so this fallback is a no-op in the browser path.
+    if (widths[s] === 0 && overrides && overrides[s]) {
+      widths[s] = overrides[s].width;
+      colors[s] = overrides[s].color;
+    } else if (colors[s] && colors[s].startsWith('var(') && overrides && overrides[s]) {
+      // Longhand case: jsdom kept the width but left the color as the
+      // literal `var(...)` string. Substitute the resolved color.
+      colors[s] = overrides[s].color;
+    }
+  }
+  return checkBorders(tag, widths, colors, parseFloat(style.borderRadius) || 0);
+}
+
+function checkElementColors(el, style, tag, window) {
+  const directText = [...el.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
+  const hasDirectText = directText.trim().length > 0;
+
+  const effectiveBg = resolveBackground(el, window);
+  return checkColors({
+    tag,
+    textColor: parseRgb(style.color),
+    bgColor: parseRgb(style.backgroundColor),
+    effectiveBg,
+    effectiveBgStops: effectiveBg ? null : resolveGradientStops(el, window),
+    fontSize: parseFloat(style.fontSize) || 16,
+    fontWeight: parseInt(style.fontWeight) || 400,
+    hasDirectText,
+    isEmojiOnly: isEmojiOnlyText(directText),
+    bgClip: style.webkitBackgroundClip || style.backgroundClip || '',
+    bgImage: style.backgroundImage || '',
+    classList: el.getAttribute?.('class') || el.className || '',
+  });
+}
+
+function checkElementIconTile(el, tag, window) {
+  if (!HEADING_TAGS.has(tag)) return [];
+  const sibling = el.previousElementSibling;
+  if (!sibling) return [];
+
+  const sibStyle = window.getComputedStyle(sibling);
+  // jsdom doesn't lay out — read explicit pixel dimensions from CSS instead.
+  const sibWidth = parseFloat(sibStyle.width) || 0;
+  const sibHeight = parseFloat(sibStyle.height) || 0;
+
+  const iconChild = sibling.querySelector('svg, i[data-lucide], i[class*="fa-"], i[class*="icon"]');
+  let iconWidth = 0;
+  if (iconChild) {
+    const iconStyle = window.getComputedStyle(iconChild);
+    iconWidth = parseFloat(iconStyle.width) || parseFloat(iconChild.getAttribute('width')) || 0;
+  }
+  // Or: tile contains an emoji/symbol character directly as its only content
+  const sibDirectText = [...sibling.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
+  const hasInlineEmojiIcon = sibling.children.length === 0 && isEmojiOnlyText(sibDirectText);
+
+  return checkIconTile({
+    headingTag: tag,
+    headingText: el.textContent || '',
+    headingTop: 0, // jsdom: no layout, skip vertical-stacking gate
+    siblingTag: sibling.tagName.toLowerCase(),
+    siblingWidth: sibWidth,
+    siblingHeight: sibHeight,
+    siblingBottom: 0,
+    siblingBgColor: parseRgb(sibStyle.backgroundColor),
+    siblingBgImage: sibStyle.backgroundImage || '',
+    siblingBorderWidth: parseFloat(sibStyle.borderTopWidth) || 0,
+    siblingBorderRadius: parseFloat(sibStyle.borderRadius) || 0,
+    hasIconChild: !!iconChild || hasInlineEmojiIcon,
+    iconChildWidth: iconWidth,
+  });
+}
+
+function checkElementMotion(tag, style) {
+  return checkMotion({
+    tag,
+    transitionProperty: style.transitionProperty || '',
+    animationName: style.animationName || '',
+    timingFunctions: [style.animationTimingFunction, style.transitionTimingFunction].filter(Boolean).join(' '),
+    classList: '',
+  });
+}
+
+function checkElementGlow(tag, style, effectiveBg) {
+  if (!style.boxShadow || style.boxShadow === 'none') return [];
+  return checkGlow({ tag, boxShadow: style.boxShadow, effectiveBg });
+}
+
+// ─── Section 6: Page-Level Checks ───────────────────────────────────────────
+
+// Browser page-level checks — use document/getComputedStyle globals
+
+function checkTypography() {
+  const findings = [];
+
+  // Walk actual text-bearing elements and tally font usage by *computed style*.
+  // This is much more accurate than scanning CSS rules — it ignores rules that
+  // exist in the stylesheet but apply to nothing (e.g. demo classes showing
+  // anti-patterns), and counts what the user actually sees.
+  const fontUsage = new Map(); // primary font name → count of elements
+  let totalTextElements = 0;
+  for (const el of document.querySelectorAll('p, h1, h2, h3, h4, h5, h6, li, td, th, dd, blockquote, figcaption, a, button, label, span')) {
+    // Skip impeccable's own elements
+    if (el.closest && el.closest('.impeccable-overlay, .impeccable-label, .impeccable-banner, .impeccable-tooltip')) continue;
+    // Only count elements that actually have visible direct text
+    const hasText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim().length > 0);
+    if (!hasText) continue;
+    const style = getComputedStyle(el);
+    const ff = style.fontFamily;
+    if (!ff) continue;
+    const stack = ff.split(',').map(f => f.trim().replace(/^['"]|['"]$/g, '').toLowerCase());
+    const primary = stack.find(f => f && !GENERIC_FONTS.has(f));
+    if (!primary) continue;
+    fontUsage.set(primary, (fontUsage.get(primary) || 0) + 1);
+    totalTextElements++;
+  }
+
+  if (totalTextElements >= 20) {
+    // A font is "primary" if it's used by at least 15% of text elements
+    const PRIMARY_THRESHOLD = 0.15;
+    for (const [font, count] of fontUsage) {
+      const share = count / totalTextElements;
+      if (share < PRIMARY_THRESHOLD) continue;
+      if (!OVERUSED_FONTS.has(font)) continue;
+      if (isBrandFontOnOwnDomain(font)) continue;
+      findings.push({ type: 'overused-font', detail: `Primary font: ${font} (${Math.round(share * 100)}% of text)` });
+    }
+
+    // Single-font check: only one distinct primary font across all text
+    if (fontUsage.size === 1) {
+      const only = [...fontUsage.keys()][0];
+      findings.push({ type: 'single-font', detail: `only font used is ${only}` });
+    }
+  }
+
+  const sizes = new Set();
+  for (const el of document.querySelectorAll('h1,h2,h3,h4,h5,h6,p,span,a,li,td,th,label,button,div')) {
+    const fs = parseFloat(getComputedStyle(el).fontSize);
+    if (fs > 0 && fs < 200) sizes.add(Math.round(fs * 10) / 10);
+  }
+  if (sizes.size >= 3) {
+    const sorted = [...sizes].sort((a, b) => a - b);
+    const ratio = sorted[sorted.length - 1] / sorted[0];
+    if (ratio < 2.0) {
+      findings.push({ type: 'flat-type-hierarchy', detail: `Sizes: ${sorted.map(s => s + 'px').join(', ')} (ratio ${ratio.toFixed(1)}:1)` });
+    }
+  }
+
+  return findings;
+}
+
+function isCardLikeDOM(el) {
+  const tag = el.tagName.toLowerCase();
+  if (SAFE_TAGS.has(tag) || ['input','select','textarea','img','video','canvas','picture'].includes(tag)) return false;
+  const style = getComputedStyle(el);
+  const cls = el.getAttribute('class') || '';
+  const hasShadow = (style.boxShadow && style.boxShadow !== 'none') || /\bshadow(?:-sm|-md|-lg|-xl|-2xl)?\b/.test(cls);
+  const hasBorder = /\bborder\b/.test(cls);
+  const hasRadius = parseFloat(style.borderRadius) > 0 || /\brounded(?:-sm|-md|-lg|-xl|-2xl|-full)?\b/.test(cls);
+  const hasBg = (style.backgroundColor && style.backgroundColor !== 'rgba(0, 0, 0, 0)') || /\bbg-(?:white|gray-\d+|slate-\d+)\b/.test(cls);
+  return isCardLikeFromProps(hasShadow, hasBorder, hasRadius, hasBg);
+}
+
+function checkLayout() {
+  const findings = [];
+  const flaggedEls = new Set();
+
+  for (const el of document.querySelectorAll('*')) {
+    if (!isCardLikeDOM(el) || flaggedEls.has(el)) continue;
+    const cls = el.getAttribute('class') || '';
+    const style = getComputedStyle(el);
+    if (style.position === 'absolute' || style.position === 'fixed') continue;
+    if (/\b(?:dropdown|popover|tooltip|menu|modal|dialog)\b/i.test(cls)) continue;
+    if ((el.textContent?.trim().length || 0) < 10) continue;
+    const rect = el.getBoundingClientRect();
+    if (rect.width < 50 || rect.height < 30) continue;
+
+    let parent = el.parentElement;
+    while (parent) {
+      if (isCardLikeDOM(parent)) { flaggedEls.add(el); break; }
+      parent = parent.parentElement;
+    }
+  }
+
+  for (const el of flaggedEls) {
+    let isAncestor = false;
+    for (const other of flaggedEls) {
+      if (other !== el && el.contains(other)) { isAncestor = true; break; }
+    }
+    if (!isAncestor) findings.push({ type: 'nested-cards', detail: 'Card inside card', el });
+  }
+
+  return findings;
+}
+
+// Node page-level checks — take document/window as parameters
+
+function checkPageTypography(doc, win) {
+  const findings = [];
+
+  const fonts = new Set();
+  const overusedFound = new Set();
+
+  for (const sheet of doc.styleSheets) {
+    let rules;
+    try { rules = sheet.cssRules || sheet.rules; } catch { continue; }
+    if (!rules) continue;
+    for (const rule of rules) {
+      if (rule.type !== 1) continue;
+      const ff = rule.style?.fontFamily;
+      if (!ff) continue;
+      const stack = ff.split(',').map(f => f.trim().replace(/^['"]|['"]$/g, '').toLowerCase());
+      const primary = stack.find(f => f && !GENERIC_FONTS.has(f));
+      if (primary) {
+        fonts.add(primary);
+        if (OVERUSED_FONTS.has(primary)) overusedFound.add(primary);
+      }
+    }
+  }
+
+  // Check Google Fonts links in HTML
+  const html = doc.documentElement?.outerHTML || '';
+  const gfRe = /fonts\.googleapis\.com\/css2?\?family=([^&"'\s]+)/gi;
+  let m;
+  while ((m = gfRe.exec(html)) !== null) {
+    const families = m[1].split('|').map(f => f.split(':')[0].replace(/\+/g, ' ').toLowerCase());
+    for (const f of families) {
+      fonts.add(f);
+      if (OVERUSED_FONTS.has(f)) overusedFound.add(f);
+    }
+  }
+
+  // Also parse raw HTML/style content for font-family (jsdom may not expose all via CSSOM)
+  const ffRe = /font-family\s*:\s*([^;}]+)/gi;
+  let fm;
+  while ((fm = ffRe.exec(html)) !== null) {
+    for (const f of fm[1].split(',').map(f => f.trim().replace(/^['"]|['"]$/g, '').toLowerCase())) {
+      if (f && !GENERIC_FONTS.has(f)) {
+        fonts.add(f);
+        if (OVERUSED_FONTS.has(f)) overusedFound.add(f);
+      }
+    }
+  }
+
+  for (const font of overusedFound) {
+    findings.push({ id: 'overused-font', snippet: `Primary font: ${font}` });
+  }
+
+  // Single font
+  if (fonts.size === 1) {
+    const els = doc.querySelectorAll('*');
+    if (els.length >= 20) {
+      findings.push({ id: 'single-font', snippet: `only font used is ${[...fonts][0]}` });
+    }
+  }
+
+  // Flat type hierarchy
+  const sizes = new Set();
+  const textEls = doc.querySelectorAll('h1, h2, h3, h4, h5, h6, p, span, a, li, td, th, label, button, div');
+  for (const el of textEls) {
+    const fontSize = parseFloat(win.getComputedStyle(el).fontSize);
+    // Filter out sub-8px values (jsdom doesn't resolve relative units properly)
+    if (fontSize >= 8 && fontSize < 200) sizes.add(Math.round(fontSize * 10) / 10);
+  }
+  if (sizes.size >= 3) {
+    const sorted = [...sizes].sort((a, b) => a - b);
+    const ratio = sorted[sorted.length - 1] / sorted[0];
+    if (ratio < 2.0) {
+      findings.push({ id: 'flat-type-hierarchy', snippet: `Sizes: ${sorted.map(s => s + 'px').join(', ')} (ratio ${ratio.toFixed(1)}:1)` });
+    }
+  }
+
+  return findings;
+}
+
+function isCardLike(el, win) {
+  const tag = el.tagName.toLowerCase();
+  if (SAFE_TAGS.has(tag) || ['input', 'select', 'textarea', 'img', 'video', 'canvas', 'picture'].includes(tag)) return false;
+
+  const style = win.getComputedStyle(el);
+  const rawStyle = el.getAttribute?.('style') || '';
+  const cls = el.getAttribute?.('class') || '';
+
+  const hasShadow = (style.boxShadow && style.boxShadow !== 'none') ||
+    /\bshadow(?:-sm|-md|-lg|-xl|-2xl)?\b/.test(cls) || /box-shadow/i.test(rawStyle);
+  const hasBorder = /\bborder\b/.test(cls);
+  const hasRadius = (parseFloat(style.borderRadius) || 0) > 0 ||
+    /\brounded(?:-sm|-md|-lg|-xl|-2xl|-full)?\b/.test(cls) || /border-radius/i.test(rawStyle);
+  const hasBg = /\bbg-(?:white|gray-\d+|slate-\d+)\b/.test(cls) ||
+    /background(?:-color)?\s*:\s*(?!transparent)/i.test(rawStyle);
+
+  return isCardLikeFromProps(hasShadow, hasBorder, hasRadius, hasBg);
+}
+
+function checkPageLayout(doc, win) {
+  const findings = [];
+
+  // Nested cards
+  const allEls = doc.querySelectorAll('*');
+  const flaggedEls = new Set();
+  for (const el of allEls) {
+    if (!isCardLike(el, win)) continue;
+    if (flaggedEls.has(el)) continue;
+
+    const tag = el.tagName.toLowerCase();
+    const cls = el.getAttribute?.('class') || '';
+    const rawStyle = el.getAttribute?.('style') || '';
+
+    if (['pre', 'code'].includes(tag)) continue;
+    if (/\b(?:absolute|fixed)\b/.test(cls) || /position\s*:\s*(?:absolute|fixed)/i.test(rawStyle)) continue;
+    if ((el.textContent?.trim().length || 0) < 10) continue;
+    if (/\b(?:dropdown|popover|tooltip|menu|modal|dialog)\b/i.test(cls)) continue;
+
+    // Walk up to find card-like ancestor
+    let parent = el.parentElement;
+    while (parent) {
+      if (isCardLike(parent, win)) {
+        flaggedEls.add(el);
+        break;
+      }
+      parent = parent.parentElement;
+    }
+  }
+
+  // Only report innermost nested cards
+  for (const el of flaggedEls) {
+    let isAncestorOfFlagged = false;
+    for (const other of flaggedEls) {
+      if (other !== el && el.contains(other)) {
+        isAncestorOfFlagged = true;
+        break;
+      }
+    }
+    if (!isAncestorOfFlagged) {
+      findings.push({ id: 'nested-cards', snippet: `Card inside card (${el.tagName.toLowerCase()})` });
+    }
+  }
+
+  // Everything centered
+  const textEls = doc.querySelectorAll('h1, h2, h3, h4, h5, h6, p, li, div, button');
+  let centeredCount = 0;
+  let totalText = 0;
+  for (const el of textEls) {
+    const hasDirectText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim().length >= 3);
+    if (!hasDirectText) continue;
+    totalText++;
+
+    let cur = el;
+    let isCentered = false;
+    while (cur && cur.nodeType === 1) {
+      const rawStyle = cur.getAttribute?.('style') || '';
+      const cls = cur.getAttribute?.('class') || '';
+      if (/text-align\s*:\s*center/i.test(rawStyle) || /\btext-center\b/.test(cls)) {
+        isCentered = true;
+        break;
+      }
+      if (cur.tagName === 'BODY') break;
+      cur = cur.parentElement;
+    }
+    if (isCentered) centeredCount++;
+  }
+
+  if (totalText >= 5 && centeredCount / totalText > 0.7) {
+    findings.push({
+      id: 'everything-centered',
+      snippet: `${centeredCount}/${totalText} text elements centered (${Math.round(centeredCount / totalText * 100)}%)`,
+    });
+  }
+
+  return findings;
+}
+
+// ─── Section 7: Browser UI (IS_BROWSER only) ────────────────────────────────
+
+if (IS_BROWSER) {
+  // Detect extension mode via the script tag's data attribute or the document element fallback.
+  // currentScript is reliable for synchronously-executing scripts (which our IIFE is).
+  const _myScript = document.currentScript;
+  const EXTENSION_MODE = (_myScript && _myScript.dataset.impeccableExtension === 'true')
+    || document.documentElement.dataset.impeccableExtension === 'true';
+
+  const BRAND_COLOR = 'oklch(55% 0.25 350)';
+  const BRAND_COLOR_HOVER = 'oklch(45% 0.25 350)';
+  const LABEL_BG = BRAND_COLOR;
+  const OUTLINE_COLOR = BRAND_COLOR;
+
+  // Inject hover styles via CSS (more reliable than JS event listeners)
+  const styleEl = document.createElement('style');
+  styleEl.textContent = `
+    @keyframes impeccable-reveal {
+      from { opacity: 0; }
+      to { opacity: 1; }
+    }
+    .impeccable-overlay:not(.impeccable-banner) {
+      pointer-events: none;
+      outline: 2px solid ${OUTLINE_COLOR};
+      border-radius: 4px;
+      transition: outline-color 0.15s ease;
+      animation: impeccable-reveal 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
+      animation-play-state: paused;
+      border-top-left-radius: 0;
+    }
+    .impeccable-overlay.impeccable-visible {
+      animation-play-state: running;
+    }
+    .impeccable-overlay.impeccable-hover {
+      outline-color: ${BRAND_COLOR_HOVER};
+      z-index: 100001 !important;
+    }
+    .impeccable-overlay.impeccable-hover .impeccable-label {
+      background: ${BRAND_COLOR_HOVER};
+    }
+    .impeccable-overlay.impeccable-spotlight {
+      z-index: 100002 !important;
+    }
+    .impeccable-overlay.impeccable-spotlight-dimmed {
+      opacity: 0.15 !important;
+      animation: none !important;
+      filter: blur(3px);
+    }
+    .impeccable-spotlight-backdrop {
+      position: fixed;
+      top: 0; left: 0; right: 0; bottom: 0;
+      backdrop-filter: blur(3px) brightness(0.6);
+      -webkit-backdrop-filter: blur(3px) brightness(0.6);
+      pointer-events: none;
+      z-index: 99998;
+      opacity: 0;
+      outline: none !important;
+      animation: none !important;
+    }
+    .impeccable-spotlight-backdrop.impeccable-visible {
+      opacity: 1;
+    }
+    .impeccable-hidden .impeccable-overlay${EXTENSION_MODE ? '' : ':not(.impeccable-banner)'} {
+      display: none !important;
+    }
+    .impeccable-hidden .impeccable-overlay${EXTENSION_MODE ? '' : ':not(.impeccable-banner)'} {
+      display: none !important;
+    }
+  `;
+  (document.head || document.documentElement).appendChild(styleEl);
+
+  // Spotlight backdrop element (created lazily on first use)
+  let spotlightBackdrop = null;
+  let spotlightTarget = null;
+  let spotlightTimer = null;
+
+  function getSpotlightBackdrop() {
+    if (!spotlightBackdrop) {
+      spotlightBackdrop = document.createElement('div');
+      spotlightBackdrop.className = 'impeccable-spotlight-backdrop';
+      document.body.appendChild(spotlightBackdrop);
+    }
+    return spotlightBackdrop;
+  }
+
+  function updateSpotlightClipPath() {
+    if (!spotlightBackdrop || !spotlightTarget) return;
+    const r = spotlightTarget.getBoundingClientRect();
+    // Match the overlay's outer edge: element rect + 4px (2px overlay offset + 2px outline width)
+    const inset = 4;
+    const radius = 6; // outline border-radius (4) + outline width (2)
+    const x1 = r.left - inset;
+    const y1 = r.top - inset;
+    const x2 = r.right + inset;
+    const y2 = r.bottom + inset;
+    const vw = window.innerWidth;
+    const vh = window.innerHeight;
+    // Outer rect + rounded inner rect (evenodd creates a hole)
+    const path = `M0 0H${vw}V${vh}H0Z M${x1 + radius} ${y1}H${x2 - radius}A${radius} ${radius} 0 0 1 ${x2} ${y1 + radius}V${y2 - radius}A${radius} ${radius} 0 0 1 ${x2 - radius} ${y2}H${x1 + radius}A${radius} ${radius} 0 0 1 ${x1} ${y2 - radius}V${y1 + radius}A${radius} ${radius} 0 0 1 ${x1 + radius} ${y1}Z`;
+    spotlightBackdrop.style.clipPath = `path(evenodd, "${path}")`;
+  }
+
+  function showSpotlight(target) {
+    if (!target || !target.getBoundingClientRect) return;
+    // Respect the spotlightBlur setting: if disabled, don't show the backdrop
+    if (window.__IMPECCABLE_CONFIG__?.spotlightBlur === false) {
+      spotlightTarget = target;
+      return;
+    }
+    spotlightTarget = target;
+    const bd = getSpotlightBackdrop();
+    updateSpotlightClipPath();
+    bd.classList.add('impeccable-visible');
+  }
+
+  function hideSpotlight() {
+    spotlightTarget = null;
+    if (spotlightTimer) { clearTimeout(spotlightTimer); spotlightTimer = null; }
+    if (spotlightBackdrop) spotlightBackdrop.classList.remove('impeccable-visible');
+  }
+
+  function isInViewport(el) {
+    const r = el.getBoundingClientRect();
+    return r.top >= 0 && r.left >= 0 && r.bottom <= window.innerHeight && r.right <= window.innerWidth;
+  }
+
+  // Reposition spotlight on scroll/resize
+  window.addEventListener('scroll', () => {
+    if (spotlightTarget) updateSpotlightClipPath();
+  }, { passive: true });
+  window.addEventListener('resize', () => {
+    if (spotlightTarget) updateSpotlightClipPath();
+  });
+
+  const overlays = [];
+  const TYPE_LABELS = {};
+  const RULE_CATEGORY = {};
+  for (const ap of ANTIPATTERNS) {
+    TYPE_LABELS[ap.id] = ap.name.toLowerCase();
+    RULE_CATEGORY[ap.id] = ap.category || 'quality';
+  }
+
+  function isInFixedContext(el) {
+    let p = el;
+    while (p && p !== document.body) {
+      if (getComputedStyle(p).position === 'fixed') return true;
+      p = p.parentElement;
+    }
+    return false;
+  }
+
+  function positionOverlay(overlay) {
+    const el = overlay._targetEl;
+    if (!el) return;
+    const rect = el.getBoundingClientRect();
+    if (overlay._isFixed) {
+      // Viewport-relative coords for fixed targets
+      overlay.style.top = `${rect.top - 2}px`;
+      overlay.style.left = `${rect.left - 2}px`;
+    } else {
+      // Document-relative coords for normal targets
+      overlay.style.top = `${rect.top + scrollY - 2}px`;
+      overlay.style.left = `${rect.left + scrollX - 2}px`;
+    }
+    overlay.style.width = `${rect.width + 4}px`;
+    overlay.style.height = `${rect.height + 4}px`;
+  }
+
+  function repositionOverlays() {
+    for (const o of overlays) {
+      if (!o._targetEl || o.classList.contains('impeccable-banner')) continue;
+      // Skip overlays whose target is currently hidden (display: none on the overlay)
+      if (o.style.display === 'none') continue;
+      positionOverlay(o);
+    }
+  }
+
+  let resizeRAF;
+  const onResize = () => {
+    cancelAnimationFrame(resizeRAF);
+    resizeRAF = requestAnimationFrame(repositionOverlays);
+  };
+  window.addEventListener('resize', onResize);
+  // Reposition on scroll too -- catches sticky/parallax shifts
+  window.addEventListener('scroll', onResize, { passive: true });
+  // Reposition when body resizes (lazy-loaded images, dynamic content, fonts loading)
+  if (typeof ResizeObserver !== 'undefined') {
+    const bodyResizeObserver = new ResizeObserver(onResize);
+    bodyResizeObserver.observe(document.body);
+  }
+
+  // Track target element visibility via IntersectionObserver.
+  // Uses a huge rootMargin so all *rendered* elements count as intersecting,
+  // while display:none / closed <details> / hidden modals etc. do not.
+  // This is event-driven -- no polling needed.
+  let overlayIndex = 0;
+  const visibilityObserver = new IntersectionObserver((entries) => {
+    for (const entry of entries) {
+      const overlay = entry.target._impeccableOverlay;
+      if (!overlay) continue;
+      if (entry.isIntersecting) {
+        overlay.style.display = '';
+        positionOverlay(overlay);
+        if (!overlay._revealed) {
+          overlay._revealed = true;
+          if (firstScanDone) {
+            // Subsequent reveals (re-scans, scroll-into-view): instant, no animation
+            overlay.style.animation = 'none';
+          } else {
+            // Initial scan: staggered cascade reveal
+            overlay.style.animationDelay = `${Math.min((overlay._staggerIndex || 0) * 60, 600)}ms`;
+          }
+          requestAnimationFrame(() => {
+            overlay.classList.add('impeccable-visible');
+            if (overlay._checkLabel) overlay._checkLabel();
+          });
+        }
+      } else {
+        overlay.style.display = 'none';
+      }
+    }
+  }, { rootMargin: '99999px' });
+
+  // Reposition overlays after CSS transitions end (e.g. reveal animations).
+  // Listens at document level so it catches transitions on ancestor elements
+  // (the transform may be on a parent, not the flagged element itself).
+  document.addEventListener('transitionend', (e) => {
+    if (e.propertyName !== 'transform') return;
+    for (const o of overlays) {
+      if (!o._targetEl || o.classList.contains('impeccable-banner') || o.style.display === 'none') continue;
+      if (e.target === o._targetEl || e.target.contains(o._targetEl)) {
+        positionOverlay(o);
+      }
+    }
+  });
+
+  const highlight = function(el, findings) {
+    const hasSlop = findings.some(f => RULE_CATEGORY[f.type || f.id] === 'slop');
+
+    const fixed = isInFixedContext(el);
+    const rect = el.getBoundingClientRect();
+    const outline = document.createElement('div');
+    outline.className = 'impeccable-overlay';
+    outline._targetEl = el;
+    outline._isFixed = fixed;
+    Object.assign(outline.style, {
+      position: fixed ? 'fixed' : 'absolute',
+      top: fixed ? `${rect.top - 2}px` : `${rect.top + scrollY - 2}px`,
+      left: fixed ? `${rect.left - 2}px` : `${rect.left + scrollX - 2}px`,
+      width: `${rect.width + 4}px`, height: `${rect.height + 4}px`,
+      zIndex: '99999', boxSizing: 'border-box',
+    });
+
+    // Build per-finding label entries: ✦ prefix for slop
+    const entries = findings.map(f => {
+      const name = TYPE_LABELS[f.type || f.id] || f.type || f.id;
+      const prefix = RULE_CATEGORY[f.type || f.id] === 'slop' ? '\u2726 ' : '';
+      return { name: prefix + name, detail: f.detail || f.snippet };
+    });
+    const allText = entries.map(e => e.name).join(', ');
+
+    const label = document.createElement('div');
+    label.className = 'impeccable-label';
+    Object.assign(label.style, {
+      position: 'absolute', bottom: '100%', left: '-2px',
+      display: 'flex', alignItems: 'center',
+      whiteSpace: 'nowrap',
+      fontSize: '11px', fontWeight: '600', letterSpacing: '0.02em',
+      color: 'white', lineHeight: '14px',
+      background: LABEL_BG,
+      fontFamily: 'system-ui, sans-serif',
+      borderRadius: '4px 4px 0 0',
+    });
+
+    const textSpan = document.createElement('span');
+    textSpan.style.padding = '3px 8px';
+    textSpan.textContent = allText;
+    label.appendChild(textSpan);
+
+    // State for cycling mode
+    let cycleMode = false;
+    let cycleIndex = 0;
+    let isHovered = false;
+    let prevBtn, nextBtn;
+
+    function updateCycleText() {
+      const e = entries[cycleIndex];
+      textSpan.textContent = isHovered ? e.detail : e.name;
+    }
+
+    function enableCycleMode() {
+      if (cycleMode || entries.length < 2) return;
+      cycleMode = true;
+
+      const btnStyle = {
+        background: 'none', border: 'none', color: 'rgba(255,255,255,0.7)',
+        fontSize: '11px', cursor: 'pointer', padding: '3px 4px',
+        fontFamily: 'system-ui, sans-serif', lineHeight: '14px',
+        pointerEvents: 'auto',
+      };
+
+      const navGroup = document.createElement('span');
+      Object.assign(navGroup.style, {
+        display: 'inline-flex', alignItems: 'center', flexShrink: '0',
+      });
+
+      prevBtn = document.createElement('button');
+      prevBtn.textContent = '\u2039';
+      Object.assign(prevBtn.style, btnStyle);
+      prevBtn.style.paddingLeft = '6px';
+      prevBtn.addEventListener('click', (e) => {
+        e.stopPropagation();
+        cycleIndex = (cycleIndex - 1 + entries.length) % entries.length;
+        updateCycleText();
+      });
+
+      nextBtn = document.createElement('button');
+      nextBtn.textContent = '\u203A';
+      Object.assign(nextBtn.style, btnStyle);
+      nextBtn.style.paddingRight = '2px';
+      nextBtn.addEventListener('click', (e) => {
+        e.stopPropagation();
+        cycleIndex = (cycleIndex + 1) % entries.length;
+        updateCycleText();
+      });
+
+      navGroup.appendChild(prevBtn);
+      navGroup.appendChild(nextBtn);
+      label.insertBefore(navGroup, textSpan);
+      textSpan.style.padding = '3px 8px 3px 4px';
+      updateCycleText();
+    }
+
+    outline.appendChild(label);
+
+    // Start hidden; the IntersectionObserver will show it once the target is rendered
+    outline.style.display = 'none';
+    outline._staggerIndex = overlayIndex++;
+    el._impeccableOverlay = outline;
+    visibilityObserver.observe(el);
+
+    // After first paint, check label width vs outline
+    outline._checkLabel = () => {
+      if (entries.length > 1 && label.offsetWidth > outline.offsetWidth) {
+        enableCycleMode();
+      }
+    };
+
+    // Hover: show detail text, darken
+    el.addEventListener('mouseenter', () => {
+      isHovered = true;
+      outline.classList.add('impeccable-hover');
+      outline.style.outlineColor = BRAND_COLOR_HOVER;
+      label.style.background = BRAND_COLOR_HOVER;
+      if (cycleMode) {
+        updateCycleText();
+      } else {
+        textSpan.textContent = entries.map(e => e.detail).join(' | ');
+      }
+    });
+    el.addEventListener('mouseleave', () => {
+      isHovered = false;
+      outline.classList.remove('impeccable-hover');
+      outline.style.outlineColor = '';
+      label.style.background = LABEL_BG;
+      if (cycleMode) {
+        updateCycleText();
+      } else {
+        textSpan.textContent = allText;
+      }
+    });
+
+    document.body.appendChild(outline);
+    overlays.push(outline);
+  };
+
+  const showPageBanner = function(findings) {
+    if (!findings.length) return;
+    const banner = document.createElement('div');
+    banner.className = 'impeccable-overlay impeccable-banner';
+    Object.assign(banner.style, {
+      position: 'fixed', top: '0', left: '0', right: '0', zIndex: '100000',
+      background: LABEL_BG, color: 'white',
+      fontFamily: 'system-ui, sans-serif', fontSize: '13px',
+      display: 'flex', alignItems: 'center', pointerEvents: 'auto',
+      height: '36px', overflow: 'hidden', maxWidth: '100vw',
+      transform: 'translateY(-100%)',
+      transition: 'transform 0.4s cubic-bezier(0.16, 1, 0.3, 1)',
+    });
+    requestAnimationFrame(() => requestAnimationFrame(() => {
+      banner.style.transform = 'translateY(0)';
+    }));
+
+    // Scrollable findings area
+    const scrollArea = document.createElement('div');
+    Object.assign(scrollArea.style, {
+      flex: '1', minWidth: '0', overflowX: 'auto', overflowY: 'hidden',
+      display: 'flex', gap: '8px', alignItems: 'center',
+      padding: '0 12px', scrollSnapType: 'x mandatory',
+      scrollbarWidth: 'none',
+    });
+    for (const f of findings) {
+      const prefix = RULE_CATEGORY[f.type] === 'slop' ? '\u2726 ' : '';
+      const tag = document.createElement('span');
+      tag.textContent = `${prefix}${TYPE_LABELS[f.type] || f.type}: ${f.detail}`;
+      Object.assign(tag.style, {
+        background: 'rgba(255,255,255,0.15)', padding: '2px 8px',
+        borderRadius: '3px', fontSize: '12px', fontFamily: 'ui-monospace, monospace',
+        whiteSpace: 'nowrap', flexShrink: '0', scrollSnapAlign: 'start',
+      });
+      scrollArea.appendChild(tag);
+    }
+    banner.appendChild(scrollArea);
+
+    // Controls area (only in standalone mode, not extension)
+    if (!EXTENSION_MODE) {
+      const controls = document.createElement('div');
+      Object.assign(controls.style, {
+        display: 'flex', alignItems: 'center', gap: '2px',
+        padding: '0 8px', flexShrink: '0',
+      });
+
+      // Toggle visibility button
+      const toggle = document.createElement('button');
+      toggle.textContent = '\u25C9'; // circle with dot (visible state)
+      toggle.title = 'Toggle overlay visibility';
+      Object.assign(toggle.style, {
+        background: 'none', border: 'none',
+        color: 'white', fontSize: '16px', cursor: 'pointer', padding: '0 4px',
+        opacity: '0.85', transition: 'opacity 0.15s',
+      });
+      let overlaysVisible = true;
+      toggle.addEventListener('click', () => {
+        overlaysVisible = !overlaysVisible;
+        document.body.classList.toggle('impeccable-hidden', !overlaysVisible);
+        toggle.textContent = overlaysVisible ? '\u25C9' : '\u25CB'; // filled vs empty circle
+        toggle.style.opacity = overlaysVisible ? '0.85' : '0.5';
+      });
+      controls.appendChild(toggle);
+
+      // Close button
+      const close = document.createElement('button');
+      close.textContent = '\u00d7';
+      close.title = 'Dismiss banner';
+      Object.assign(close.style, {
+        background: 'none', border: 'none',
+        color: 'white', fontSize: '18px', cursor: 'pointer', padding: '0 4px',
+      });
+      close.addEventListener('click', () => banner.remove());
+      controls.appendChild(close);
+
+      banner.appendChild(controls);
+    }
+    document.body.appendChild(banner);
+    overlays.push(banner);
+  };
+
+  // Heuristic for skipping CSS-in-JS hashed class names like "css-1a2b3c" or "_2x4hG_".
+  // These change between builds and produce brittle, ugly selectors.
+  function isLikelyHashedClass(c) {
+    if (!c) return true;
+    if (/^(css|sc|emotion|jsx|module)-[\w-]{4,}$/i.test(c)) return true;
+    if (/^_[\w-]{5,}$/.test(c)) return true;
+    if (/^[a-z0-9]{6,}$/i.test(c) && /\d/.test(c)) return true;
+    return false;
+  }
+
+  function buildSelectorSegment(el) {
+    const tag = el.tagName.toLowerCase();
+    let sel = tag;
+
+    if (el.classList && el.classList.length > 0) {
+      const classes = [...el.classList]
+        .filter(c => !c.startsWith('impeccable-') && !isLikelyHashedClass(c))
+        .slice(0, 2);
+      if (classes.length > 0) {
+        sel += '.' + classes.map(c => CSS.escape(c)).join('.');
+      }
+    }
+
+    // Disambiguate among siblings only if the parent has multiple matches
+    const parent = el.parentElement;
+    if (parent) {
+      try {
+        const matching = parent.querySelectorAll(':scope > ' + sel);
+        if (matching.length > 1) {
+          const sameType = [...parent.children].filter(c => c.tagName === el.tagName);
+          const idx = sameType.indexOf(el) + 1;
+          sel += `:nth-of-type(${idx})`;
+        }
+      } catch {
+        const idx = [...parent.children].indexOf(el) + 1;
+        sel = `${tag}:nth-child(${idx})`;
+      }
+    }
+    return sel;
+  }
+
+  function generateSelector(el) {
+    if (el === document.body) return 'body';
+    if (el === document.documentElement) return 'html';
+    if (el.id) return '#' + CSS.escape(el.id);
+
+    const parts = [];
+    let current = el;
+    let depth = 0;
+    const MAX_DEPTH = 10;
+
+    while (current && current !== document.body && current !== document.documentElement && depth < MAX_DEPTH) {
+      parts.unshift(buildSelectorSegment(current));
+
+      // Anchor on an ancestor's ID and stop walking up
+      if (current.id) {
+        parts[0] = '#' + CSS.escape(current.id);
+        break;
+      }
+
+      // Stop as soon as the partial selector uniquely identifies the target
+      const trySelector = parts.join(' > ');
+      try {
+        const matches = document.querySelectorAll(trySelector);
+        if (matches.length === 1 && matches[0] === el) {
+          return trySelector;
+        }
+      } catch { /* invalid selector — keep walking */ }
+
+      current = current.parentElement;
+      depth++;
+    }
+
+    return parts.join(' > ');
+  }
+
+  function isElementHidden(el) {
+    if (!el || el === document.body || el === document.documentElement) return false;
+    if (typeof el.checkVisibility === 'function') return !el.checkVisibility({ checkOpacity: false, checkVisibilityCSS: true });
+    // Fallback: zero size or no offsetParent (covers display:none and detached subtrees)
+    return el.offsetWidth === 0 && el.offsetHeight === 0;
+  }
+
+  function serializeFindings(allFindings) {
+    return allFindings.map(({ el, findings }) => ({
+      selector: generateSelector(el),
+      tagName: el.tagName?.toLowerCase() || 'unknown',
+      rect: (el !== document.body && el !== document.documentElement && el.getBoundingClientRect)
+        ? el.getBoundingClientRect().toJSON() : null,
+      isPageLevel: el === document.body || el === document.documentElement,
+      isHidden: isElementHidden(el),
+      findings: findings.map(f => {
+        const ap = ANTIPATTERNS.find(a => a.id === (f.type || f.id));
+        return {
+          type: f.type || f.id,
+          category: ap ? ap.category : 'quality',
+          detail: f.detail || f.snippet,
+          name: ap ? ap.name : (f.type || f.id),
+          description: ap ? ap.description : '',
+        };
+      }),
+    }));
+  }
+
+  const printSummary = function(allFindings) {
+    if (allFindings.length === 0) {
+      console.log('%c[impeccable] No anti-patterns found.', 'color: #22c55e; font-weight: bold');
+      return;
+    }
+    console.group(
+      `%c[impeccable] ${allFindings.length} anti-pattern${allFindings.length === 1 ? '' : 's'} found`,
+      'color: oklch(60% 0.25 350); font-weight: bold'
+    );
+    for (const { el, findings } of allFindings) {
+      for (const f of findings) {
+        console.log(`%c${f.type || f.id}%c ${f.detail || f.snippet}`,
+          'color: oklch(55% 0.25 350); font-weight: bold', 'color: inherit', el);
+      }
+    }
+    console.groupEnd();
+  };
+
+  let firstScanDone = false;
+  const scan = function() {
+    for (const o of overlays) o.remove();
+    overlays.length = 0;
+    visibilityObserver.disconnect();
+    overlayIndex = 0;
+    const allFindings = [];
+    const _disabled = EXTENSION_MODE ? (window.__IMPECCABLE_CONFIG__?.disabledRules || []) : [];
+    const _ruleOk = (id) => !_disabled.length || !_disabled.includes(id);
+
+    for (const el of document.querySelectorAll('*')) {
+      // Skip impeccable's own elements and any descendants (overlays, labels, banner, nav buttons)
+      if (el.closest('.impeccable-overlay, .impeccable-label, .impeccable-banner, .impeccable-tooltip')) continue;
+      // Skip browser extension elements (Claude, etc.)
+      const elId = el.id || '';
+      if (elId.startsWith('claude-') || elId.startsWith('cic-')) continue;
+      // Skip html/body -- page-level findings go in the banner, not a full-page overlay
+      if (el === document.body || el === document.documentElement) continue;
+
+      const findings = [
+        ...checkElementBordersDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementColorsDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementMotionDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementGlowDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementAIPaletteDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementIconTileDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+        ...checkElementQualityDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
+      ].filter(f => _ruleOk(f.type));
+
+      if (findings.length > 0) {
+        highlight(el, findings);
+        allFindings.push({ el, findings });
+      }
+    }
+
+    const pageLevelFindings = [];
+
+    const typoFindings = checkTypography().filter(f => _ruleOk(f.type));
+    if (typoFindings.length > 0) {
+      pageLevelFindings.push(...typoFindings);
+      allFindings.push({ el: document.body, findings: typoFindings });
+    }
+
+    const layoutFindings = checkLayout().filter(f => _ruleOk(f.type));
+    for (const f of layoutFindings) {
+      const el = f.el || document.body;
+      delete f.el;
+      // Merge into existing overlay if this element already has one
+      const existing = el._impeccableOverlay;
+      if (existing) {
+        const nameRow = existing.querySelector('.impeccable-label-name');
+        const detailRow = existing.querySelector('.impeccable-label-detail');
+        const newType = TYPE_LABELS[f.type] || f.type;
+        if (nameRow) nameRow.textContent += ', ' + newType;
+        if (detailRow) detailRow.textContent += ' | ' + (f.detail || '');
+      } else {
+        highlight(el, [f]);
+      }
+      allFindings.push({ el, findings: [f] });
+    }
+
+    // Page-level quality checks (headings, etc.)
+    const qualityFindings = checkPageQualityDOM().filter(f => _ruleOk(f.type));
+    if (qualityFindings.length > 0) {
+      pageLevelFindings.push(...qualityFindings);
+      allFindings.push({ el: document.body, findings: qualityFindings });
+    }
+
+    // Regex-on-HTML checks (shared with Node)
+    const htmlPatternFindings = checkHtmlPatterns(document.documentElement.outerHTML);
+    if (htmlPatternFindings.length > 0) {
+      const mapped = htmlPatternFindings.map(f => ({ type: f.id, detail: f.snippet })).filter(f => _ruleOk(f.type));
+      pageLevelFindings.push(...mapped);
+      allFindings.push({ el: document.body, findings: mapped });
+    }
+
+    if (pageLevelFindings.length > 0) {
+      showPageBanner(pageLevelFindings);
+    }
+
+    if (!EXTENSION_MODE) printSummary(allFindings);
+
+    // In extension mode, post serialized results for the DevTools panel
+    if (EXTENSION_MODE) {
+      window.postMessage({
+        source: 'impeccable-results',
+        findings: serializeFindings(allFindings),
+        count: allFindings.length,
+      }, '*');
+    }
+
+    // After this scan completes, all subsequent reveals are instant (no stagger, no animation)
+    setTimeout(() => { firstScanDone = true; }, 1000);
+
+    return allFindings;
+  };
+
+  if (EXTENSION_MODE) {
+    // Extension mode: listen for commands, don't auto-scan
+    window.addEventListener('message', (e) => {
+      if (e.source !== window || !e.data || e.data.source !== 'impeccable-command') return;
+      if (e.data.action === 'scan') {
+        if (e.data.config) window.__IMPECCABLE_CONFIG__ = e.data.config;
+        scan();
+      }
+      if (e.data.action === 'toggle-overlays') {
+        const visible = !document.body.classList.contains('impeccable-hidden');
+        document.body.classList.toggle('impeccable-hidden', visible);
+        window.postMessage({ source: 'impeccable-overlays-toggled', visible: !visible }, '*');
+      }
+      if (e.data.action === 'remove') {
+        for (const o of overlays) o.remove();
+        overlays.length = 0;
+        visibilityObserver.disconnect();
+        styleEl.remove();
+        if (spotlightBackdrop) { spotlightBackdrop.remove(); spotlightBackdrop = null; }
+        document.body.classList.remove('impeccable-hidden');
+      }
+      if (e.data.action === 'highlight') {
+        if (spotlightTimer) { clearTimeout(spotlightTimer); spotlightTimer = null; }
+        try {
+          const target = e.data.selector ? document.querySelector(e.data.selector) : null;
+          if (target) {
+            // Scroll first so positionOverlay reads the post-scroll rect
+            if (!isInViewport(target) && target.scrollIntoView) {
+              target.scrollIntoView({ behavior: 'instant', block: 'center' });
+            }
+            for (const o of overlays) {
+              if (o.classList.contains('impeccable-banner')) continue;
+              const isMatch = o._targetEl === target;
+              o.classList.toggle('impeccable-spotlight', isMatch);
+              o.classList.toggle('impeccable-spotlight-dimmed', !isMatch);
+              if (isMatch) {
+                // Force the matching overlay visible immediately, don't wait for IntersectionObserver
+                o.style.display = '';
+                o.style.animation = 'none';
+                o.classList.add('impeccable-visible');
+                o._revealed = true;
+                positionOverlay(o);
+              }
+            }
+            showSpotlight(target);
+          }
+        } catch { /* invalid selector */ }
+      }
+      if (e.data.action === 'unhighlight') {
+        hideSpotlight();
+        for (const o of overlays) {
+          o.classList.remove('impeccable-spotlight');
+          o.classList.remove('impeccable-spotlight-dimmed');
+        }
+      }
+    });
+    window.postMessage({ source: 'impeccable-ready' }, '*');
+  } else {
+    if (document.readyState === 'loading') {
+      document.addEventListener('DOMContentLoaded', () => setTimeout(scan, 100));
+    } else {
+      setTimeout(scan, 100);
+    }
+  }
+
+  window.impeccableScan = scan;
+}
+
+// ─── Section 8: Node Engine ─────────────────────────────────────────────────
+// @browser-strip-start
+
+function getAP(id) {
+  return ANTIPATTERNS.find(a => a.id === id);
+}
+
+function finding(id, filePath, snippet, line = 0) {
+  const ap = getAP(id);
+  return { antipattern: id, name: ap.name, description: ap.description, file: filePath, line, snippet };
+}
+
+/** Check if content looks like a full page (not a component/partial) */
+function isFullPage(content) {
+  const stripped = content.replace(/<!--[\s\S]*?-->/g, '');
+  return /<!doctype\s|<html[\s>]|<head[\s>]/i.test(stripped);
+}
+
+// ---------------------------------------------------------------------------
+// jsdom CSS-variable border override map
+// ---------------------------------------------------------------------------
+//
+// jsdom's CSSOM silently drops any border shorthand that contains a var()
+// reference — the computed style for the element then shows empty width,
+// empty style, and a default black color. That's enough to hide the most
+// common real-world side-tab pattern in AI-generated pages:
+//
+//   :root { --brand: #87a8ff; }
+//   .card { border-left: 5px solid var(--brand); border-radius: 4px; }
+//
+// Real browsers (and therefore the browser detector path) resolve var()
+// natively, so this only affects the Node jsdom path.
+//
+// This pre-pass walks the stylesheets, finds any rule whose per-side or
+// all-sides border property contains var(), resolves the var() against
+// :root-level custom properties (read from the documentElement's computed
+// style, which jsdom DOES handle correctly), and attaches the resolved
+// width+color to every element that matches the rule's selector. The
+// Node-side `checkElementBorders` adapter consumes that map as a fallback
+// whenever jsdom's computed style came back empty.
+//
+// Limitations (intentional, to keep the pass simple):
+//   * Only :root-level custom properties are resolved. Scoped overrides on
+//     descendants are not tracked — uncommon in practice and would require
+//     a per-element cascade walk.
+//   * @media / @supports wrapped rules are ignored (jsdom often mishandles
+//     these anyway).
+//   * The fallback only fills sides that jsdom left empty, so any rule
+//     whose border parses normally still wins via the computed style.
+
+const BORDER_SHORTHAND_RE = /^(\d+(?:\.\d+)?)px\s+(solid|dashed|dotted|double|groove|ridge|inset|outset)\s+(.+)$/i;
+
+// isNeutralColor only understands rgba()/oklch()/lch()/lab()/hsl()/hwb().
+// CSS variables typically hold hex or named colors, so normalize those to
+// rgb() before handing the value off to the shared check. Anything we don't
+// recognise is passed through unchanged — isNeutralColor then treats it as
+// non-neutral, which is the safer default (matches the oklch-era bugfix).
+const NAMED_COLORS = {
+  white: [255, 255, 255], black: [0, 0, 0], gray: [128, 128, 128],
+  grey: [128, 128, 128], silver: [192, 192, 192], red: [255, 0, 0],
+  green: [0, 128, 0], blue: [0, 0, 255], yellow: [255, 255, 0],
+};
+
+function normalizeColorForCheck(value) {
+  if (!value) return value;
+  const v = value.trim();
+  const hex6 = v.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i);
+  if (hex6) {
+    const [r, g, b] = [parseInt(hex6[1], 16), parseInt(hex6[2], 16), parseInt(hex6[3], 16)];
+    return `rgb(${r}, ${g}, ${b})`;
+  }
+  const hex3 = v.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i);
+  if (hex3) {
+    const [r, g, b] = [
+      parseInt(hex3[1] + hex3[1], 16),
+      parseInt(hex3[2] + hex3[2], 16),
+      parseInt(hex3[3] + hex3[3], 16),
+    ];
+    return `rgb(${r}, ${g}, ${b})`;
+  }
+  const named = NAMED_COLORS[v.toLowerCase()];
+  if (named) return `rgb(${named[0]}, ${named[1]}, ${named[2]})`;
+  return v;
+}
+
+function buildBorderOverrideMap(document, window) {
+  const map = new Map();
+  const rootStyle = window.getComputedStyle(document.documentElement);
+
+  function resolveVar(value, depth = 0) {
+    if (!value || depth > 10 || !value.includes('var(')) return value;
+    return value.replace(
+      /var\(\s*(--[\w-]+)\s*(?:,\s*([^)]+))?\s*\)/g,
+      (_, name, fallback) => {
+        const v = rootStyle.getPropertyValue(name).trim();
+        if (v) return resolveVar(v, depth + 1);
+        if (fallback) return resolveVar(fallback.trim(), depth + 1);
+        return '';
+      }
+    );
+  }
+
+  function parseShorthand(text) {
+    const m = text.trim().match(BORDER_SHORTHAND_RE);
+    if (!m) return null;
+    return { width: parseFloat(m[1]), color: normalizeColorForCheck(m[3]) };
+  }
+
+  // Read from the per-property accessors on rule.style. jsdom preserves
+  // each border-* shorthand it parsed, even when the overall cssText has
+  // been truncated (e.g. a `border: 1px solid var(...)` followed by a
+  // `border-left: ...` loses the first declaration but keeps the second).
+  const SIDE_PROPS = [
+    ['borderLeft', 'Left'],
+    ['borderRight', 'Right'],
+    ['borderTop', 'Top'],
+    ['borderBottom', 'Bottom'],
+    ['borderInlineStart', 'Left'],
+    ['borderInlineEnd', 'Right'],
+  ];
+
+  for (const sheet of document.styleSheets) {
+    let rules;
+    try { rules = sheet.cssRules || []; } catch { continue; }
+    for (const rule of rules) {
+      // CSSStyleRule only; skip @media / @keyframes / @supports wrappers.
+      if (rule.type !== 1 || !rule.style || !rule.selectorText) continue;
+
+      const perSide = {};
+
+      for (const [prop, side] of SIDE_PROPS) {
+        const val = rule.style[prop];
+        if (!val || !val.includes('var(')) continue;
+        const parsed = parseShorthand(resolveVar(val));
+        if (parsed && parsed.color) perSide[side] = parsed;
+      }
+
+      // Uniform `border: <w> <style> var(...)` applies to every side the
+      // per-side map didn't already claim.
+      const borderAll = rule.style.border;
+      if (borderAll && borderAll.includes('var(')) {
+        const parsed = parseShorthand(resolveVar(borderAll));
+        if (parsed && parsed.color) {
+          for (const s of ['Top', 'Right', 'Bottom', 'Left']) {
+            if (!perSide[s]) perSide[s] = parsed;
+          }
+        }
+      }
+
+      // Longhand `border-*-color: var(...)` with width/style in separate
+      // declarations. Rare in AI-generated pages, but cheap to cover.
+      for (const [prop, side] of [
+        ['borderLeftColor', 'Left'],
+        ['borderRightColor', 'Right'],
+        ['borderTopColor', 'Top'],
+        ['borderBottomColor', 'Bottom'],
+      ]) {
+        const val = rule.style[prop];
+        if (!val || !val.includes('var(')) continue;
+        const resolved = resolveVar(val).trim();
+        if (!resolved) continue;
+        // Width may or may not come from this rule — that's fine; the
+        // adapter only substitutes the color when jsdom left it as a
+        // literal var() string.
+        if (!perSide[side]) perSide[side] = { width: 0, color: normalizeColorForCheck(resolved) };
+      }
+
+      if (Object.keys(perSide).length === 0) continue;
+
+      let matched;
+      try { matched = document.querySelectorAll(rule.selectorText); }
+      catch { continue; }
+
+      for (const el of matched) {
+        const existing = map.get(el);
+        if (existing) {
+          // Later rules overwrite earlier ones — approximates source-order
+          // cascade for equal-specificity rules and is good enough for the
+          // uncontested var()-dropped sides we're trying to recover.
+          Object.assign(existing, perSide);
+        } else {
+          map.set(el, { ...perSide });
+        }
+      }
+    }
+  }
+
+  return map;
+}
+
+// ---------------------------------------------------------------------------
+// jsdom detection (default for HTML files)
+// ---------------------------------------------------------------------------
+
+async function detectHtml(filePath) {
+  let JSDOM;
+  try {
+    ({ JSDOM } = await import('jsdom'));
+  } catch {
+    const content = fs.readFileSync(filePath, 'utf-8');
+    return detectText(content, filePath);
+  }
+
+  const html = fs.readFileSync(filePath, 'utf-8');
+  const resolvedPath = path.resolve(filePath);
+  const fileDir = path.dirname(resolvedPath);
+
+  // Inline linked local stylesheets so jsdom can see them
+  let processedHtml = html;
+  const linkRes = [
+    /<link[^>]+rel=["']stylesheet["'][^>]*href=["']([^"']+)["'][^>]*>/gi,
+    /<link[^>]+href=["']([^"']+)["'][^>]*rel=["']stylesheet["'][^>]*>/gi,
+  ];
+  for (const re of linkRes) {
+    let m;
+    while ((m = re.exec(html)) !== null) {
+      const href = m[1];
+      if (/^(https?:)?\/\//.test(href)) continue;
+      const cssPath = path.resolve(fileDir, href);
+      try {
+        const css = fs.readFileSync(cssPath, 'utf-8');
+        processedHtml = processedHtml.replace(m[0], `<style>/* ${href} */\n${css}\n</style>`);
+      } catch { /* skip unreadable */ }
+    }
+  }
+
+  const dom = new JSDOM(processedHtml, {
+    url: `file://${resolvedPath}`,
+  });
+  const { window } = dom;
+  const { document } = window;
+
+  const findings = [];
+
+  // Pre-pass: recover border declarations that jsdom dropped because they
+  // contained a var() reference. The map is keyed by element and consulted
+  // by the border check adapter as a fallback.
+  const borderOverrides = buildBorderOverrideMap(document, window);
+
+  // Element-level checks (borders + colors + motion)
+  for (const el of document.querySelectorAll('*')) {
+    const tag = el.tagName.toLowerCase();
+    const style = window.getComputedStyle(el);
+    for (const f of checkElementBorders(tag, style, borderOverrides.get(el))) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+    for (const f of checkElementColors(el, style, tag, window)) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+    for (const f of checkElementGlow(tag, style, resolveBackground(el.parentElement || el, window))) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+    for (const f of checkElementMotion(tag, style)) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+    for (const f of checkElementIconTile(el, tag, window)) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+    for (const f of checkElementQuality(el, style, tag, window)) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+  }
+
+  // Page-level checks (only for full pages, not partials)
+  if (isFullPage(html)) {
+    for (const f of checkPageTypography(document, window)) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+    for (const f of checkPageLayout(document, window)) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+    for (const f of checkPageQualityFromDoc(document)) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+    for (const f of checkHtmlPatterns(html)) {
+      findings.push(finding(f.id, filePath, f.snippet));
+    }
+  }
+
+  window.close();
+  return findings;
+}
+
+// ---------------------------------------------------------------------------
+// Puppeteer detection (for URLs)
+// ---------------------------------------------------------------------------
+
+async function detectUrl(url) {
+  let puppeteer;
+  try {
+    puppeteer = await import('puppeteer');
+  } catch {
+    throw new Error('puppeteer is required for URL scanning. Install: npm install puppeteer');
+  }
+
+  // Read the browser detection script — reuse it instead of reimplementing
+  const browserScriptPath = path.resolve(
+    path.dirname(new URL(import.meta.url).pathname),
+    'detect-antipatterns-browser.js'
+  );
+  let browserScript;
+  try {
+    browserScript = fs.readFileSync(browserScriptPath, 'utf-8');
+  } catch {
+    throw new Error(`Browser script not found at ${browserScriptPath}`);
+  }
+
+  // CI runners (GitHub Actions Ubuntu) block unprivileged user namespaces, so
+  // Chrome can't initialize its sandbox there. Disable the sandbox only when
+  // running in CI; local users keep the default hardened launch.
+  const launchArgs = process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox'] : [];
+  const browser = await puppeteer.default.launch({ headless: true, args: launchArgs });
+  const page = await browser.newPage();
+  await page.setViewport({ width: 1280, height: 800 });
+  await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
+
+  // Inject the browser detection script and collect results
+  await page.evaluate(browserScript);
+  const results = await page.evaluate(() => {
+    if (!window.impeccableScan) return [];
+    const allFindings = window.impeccableScan();
+    return allFindings.flatMap(({ findings }) =>
+      findings.map(f => ({ id: f.type, snippet: f.detail }))
+    );
+  });
+
+  await browser.close();
+  return results.map(f => finding(f.id, url, f.snippet));
+}
+
+// ---------------------------------------------------------------------------
+// Regex fallback (non-HTML files: CSS, JSX, TSX, etc.)
+// ---------------------------------------------------------------------------
+
+const hasRounded = (line) => /\brounded(?:-\w+)?\b/.test(line);
+const hasBorderRadius = (line) => /border-radius/i.test(line);
+const isSafeElement = (line) => /<(?:blockquote|nav[\s>]|pre[\s>]|code[\s>]|a\s|input[\s>]|span[\s>])/i.test(line);
+
+function isNeutralBorderColor(str) {
+  const m = str.match(/solid\s+(#[0-9a-f]{3,8}|rgba?\([^)]+\)|\w+)/i);
+  if (!m) return false;
+  const c = m[1].toLowerCase();
+  if (['gray', 'grey', 'silver', 'white', 'black', 'transparent', 'currentcolor'].includes(c)) return true;
+  const hex = c.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/);
+  if (hex) {
+    const [r, g, b] = [parseInt(hex[1], 16), parseInt(hex[2], 16), parseInt(hex[3], 16)];
+    return (Math.max(r, g, b) - Math.min(r, g, b)) < 30;
+  }
+  const shex = c.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/);
+  if (shex) {
+    const [r, g, b] = [parseInt(shex[1] + shex[1], 16), parseInt(shex[2] + shex[2], 16), parseInt(shex[3] + shex[3], 16)];
+    return (Math.max(r, g, b) - Math.min(r, g, b)) < 30;
+  }
+  return false;
+}
+
+const REGEX_MATCHERS = [
+  // --- Side-tab ---
+  { id: 'side-tab', regex: /\bborder-[lrse]-(\d+)\b/g,
+    test: (m, line) => { const n = +m[1]; return hasRounded(line) ? n >= 1 : n >= 4; },
+    fmt: (m) => m[0] },
+  { id: 'side-tab', regex: /border-(?:left|right)\s*:\s*(\d+)px\s+solid[^;]*/gi,
+    test: (m, line) => { if (isSafeElement(line)) return false; if (isNeutralBorderColor(m[0])) return false; const n = +m[1]; return hasBorderRadius(line) ? n >= 1 : n >= 3; },
+    fmt: (m) => m[0].replace(/\s*;?\s*$/, '') },
+  { id: 'side-tab', regex: /border-(?:left|right)-width\s*:\s*(\d+)px/gi,
+    test: (m, line) => !isSafeElement(line) && +m[1] >= 3,
+    fmt: (m) => m[0] },
+  { id: 'side-tab', regex: /border-inline-(?:start|end)\s*:\s*(\d+)px\s+solid/gi,
+    test: (m, line) => !isSafeElement(line) && +m[1] >= 3,
+    fmt: (m) => m[0] },
+  { id: 'side-tab', regex: /border-inline-(?:start|end)-width\s*:\s*(\d+)px/gi,
+    test: (m, line) => !isSafeElement(line) && +m[1] >= 3,
+    fmt: (m) => m[0] },
+  { id: 'side-tab', regex: /border(?:Left|Right)\s*[:=]\s*["'`](\d+)px\s+solid/g,
+    test: (m) => +m[1] >= 3,
+    fmt: (m) => m[0] },
+  // --- Border accent on rounded ---
+  { id: 'border-accent-on-rounded', regex: /\bborder-[tb]-(\d+)\b/g,
+    test: (m, line) => hasRounded(line) && +m[1] >= 1,
+    fmt: (m) => m[0] },
+  { id: 'border-accent-on-rounded', regex: /border-(?:top|bottom)\s*:\s*(\d+)px\s+solid/gi,
+    test: (m, line) => +m[1] >= 3 && hasBorderRadius(line),
+    fmt: (m) => m[0] },
+  // --- Overused font ---
+  { id: 'overused-font', regex: /font-family\s*:\s*['"]?(Inter|Roboto|Open Sans|Lato|Montserrat|Arial|Helvetica)\b/gi,
+    test: () => true,
+    fmt: (m) => m[0] },
+  { id: 'overused-font', regex: /fonts\.googleapis\.com\/css2?\?family=(Inter|Roboto|Open\+Sans|Lato|Montserrat)\b/gi,
+    test: () => true,
+    fmt: (m) => `Google Fonts: ${m[1].replace(/\+/g, ' ')}` },
+  // --- Pure black background ---
+  { id: 'pure-black-white', regex: /background(?:-color)?\s*:\s*(#000000|#000|rgb\(0,\s*0,\s*0\))\b/gi,
+    test: () => true,
+    fmt: (m) => m[0] },
+  // --- Gradient text ---
+  { id: 'gradient-text', regex: /background-clip\s*:\s*text|-webkit-background-clip\s*:\s*text/gi,
+    test: (m, line) => /gradient/i.test(line),
+    fmt: () => 'background-clip: text + gradient' },
+  // --- Gradient text (Tailwind) ---
+  { id: 'gradient-text', regex: /\bbg-clip-text\b/g,
+    test: (m, line) => /\bbg-gradient-to-/i.test(line),
+    fmt: () => 'bg-clip-text + bg-gradient' },
+  // --- Tailwind pure black background ---
+  { id: 'pure-black-white', regex: /\bbg-black\b/g,
+    test: () => true,
+    fmt: (m) => m[0] },
+  // --- Tailwind gray on colored bg ---
+  { id: 'gray-on-color', regex: /\btext-(?:gray|slate|zinc|neutral|stone)-(\d+)\b/g,
+    test: (m, line) => /\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/.test(line),
+    fmt: (m, line) => { const bg = line.match(/\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/); return `${m[0]} on ${bg?.[0] || '?'}`; } },
+  // --- Tailwind AI palette ---
+  { id: 'ai-color-palette', regex: /\btext-(?:purple|violet|indigo)-(\d+)\b/g,
+    test: (m, line) => /\btext-(?:[2-9]xl|[3-9]xl)\b|<h[1-3]/i.test(line),
+    fmt: (m) => `${m[0]} on heading` },
+  { id: 'ai-color-palette', regex: /\bfrom-(?:purple|violet|indigo)-(\d+)\b/g,
+    test: (m, line) => /\bto-(?:purple|violet|indigo|blue|cyan|pink|fuchsia)-\d+\b/.test(line),
+    fmt: (m) => `${m[0]} gradient` },
+  // --- Bounce/elastic easing ---
+  { id: 'bounce-easing', regex: /\banimate-bounce\b/g,
+    test: () => true,
+    fmt: () => 'animate-bounce (Tailwind)' },
+  { id: 'bounce-easing', regex: /animation(?:-name)?\s*:\s*[^;]*\b(bounce|elastic|wobble|jiggle|spring)\b/gi,
+    test: () => true,
+    fmt: (m) => m[0] },
+  { id: 'bounce-easing', regex: /cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)/g,
+    test: (m) => {
+      const y1 = parseFloat(m[2]), y2 = parseFloat(m[4]);
+      return y1 < -0.1 || y1 > 1.1 || y2 < -0.1 || y2 > 1.1;
+    },
+    fmt: (m) => `cubic-bezier(${m[1]}, ${m[2]}, ${m[3]}, ${m[4]})` },
+  // --- Layout property transition ---
+  { id: 'layout-transition', regex: /transition\s*:\s*([^;{}]+)/gi,
+    test: (m) => {
+      const val = m[1].toLowerCase();
+      if (/\ball\b/.test(val)) return false;
+      return /\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding\b|\bmargin\b/.test(val);
+    },
+    fmt: (m) => {
+      const found = m[1].match(/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi);
+      return `transition: ${found ? found.join(', ') : m[1].trim()}`;
+    } },
+  { id: 'layout-transition', regex: /transition-property\s*:\s*([^;{}]+)/gi,
+    test: (m) => {
+      const val = m[1].toLowerCase();
+      if (/\ball\b/.test(val)) return false;
+      return /\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding\b|\bmargin\b/.test(val);
+    },
+    fmt: (m) => {
+      const found = m[1].match(/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi);
+      return `transition-property: ${found ? found.join(', ') : m[1].trim()}`;
+    } },
+];
+
+const REGEX_ANALYZERS = [
+  // Single font
+  (content, filePath) => {
+    const fontFamilyRe = /font-family\s*:\s*([^;}]+)/gi;
+    const fonts = new Set();
+    let m;
+    while ((m = fontFamilyRe.exec(content)) !== null) {
+      for (const f of m[1].split(',').map(f => f.trim().replace(/^['"]|['"]$/g, '').toLowerCase())) {
+        if (f && !GENERIC_FONTS.has(f)) fonts.add(f);
+      }
+    }
+    const gfRe = /fonts\.googleapis\.com\/css2?\?family=([^&"'\s]+)/gi;
+    while ((m = gfRe.exec(content)) !== null) {
+      for (const f of m[1].split('|').map(f => f.split(':')[0].replace(/\+/g, ' ').toLowerCase())) fonts.add(f);
+    }
+    if (fonts.size !== 1 || content.split('\n').length < 20) return [];
+    const name = [...fonts][0];
+    const lines = content.split('\n');
+    let line = 1;
+    for (let i = 0; i < lines.length; i++) { if (lines[i].toLowerCase().includes(name)) { line = i + 1; break; } }
+    return [finding('single-font', filePath, `only font used is ${name}`, line)];
+  },
+  // Flat type hierarchy
+  (content, filePath) => {
+    const sizes = new Set();
+    const REM = 16;
+    let m;
+    const sizeRe = /font-size\s*:\s*([\d.]+)(px|rem|em)\b/gi;
+    while ((m = sizeRe.exec(content)) !== null) {
+      const px = m[2] === 'px' ? +m[1] : +m[1] * REM;
+      if (px > 0 && px < 200) sizes.add(Math.round(px * 10) / 10);
+    }
+    const clampRe = /font-size\s*:\s*clamp\(\s*([\d.]+)(px|rem|em)\s*,\s*[^,]+,\s*([\d.]+)(px|rem|em)\s*\)/gi;
+    while ((m = clampRe.exec(content)) !== null) {
+      sizes.add(Math.round((m[2] === 'px' ? +m[1] : +m[1] * REM) * 10) / 10);
+      sizes.add(Math.round((m[4] === 'px' ? +m[3] : +m[3] * REM) * 10) / 10);
+    }
+    const TW = { 'text-xs': 12, 'text-sm': 14, 'text-base': 16, 'text-lg': 18, 'text-xl': 20, 'text-2xl': 24, 'text-3xl': 30, 'text-4xl': 36, 'text-5xl': 48, 'text-6xl': 60, 'text-7xl': 72, 'text-8xl': 96, 'text-9xl': 128 };
+    for (const [cls, px] of Object.entries(TW)) { if (new RegExp(`\\b${cls}\\b`).test(content)) sizes.add(px); }
+    if (sizes.size < 3) return [];
+    const sorted = [...sizes].sort((a, b) => a - b);
+    const ratio = sorted[sorted.length - 1] / sorted[0];
+    if (ratio >= 2.0) return [];
+    const lines = content.split('\n');
+    let line = 1;
+    for (let i = 0; i < lines.length; i++) { if (/font-size/i.test(lines[i]) || /\btext-(?:xs|sm|base|lg|xl|\d)/i.test(lines[i])) { line = i + 1; break; } }
+    return [finding('flat-type-hierarchy', filePath, `Sizes: ${sorted.map(s => s + 'px').join(', ')} (ratio ${ratio.toFixed(1)}:1)`, line)];
+  },
+  // Monotonous spacing (regex)
+  (content, filePath) => {
+    const vals = [];
+    let m;
+    const pxRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*(\d+)px/gi;
+    while ((m = pxRe.exec(content)) !== null) { const v = +m[1]; if (v > 0 && v < 200) vals.push(v); }
+    const remRe = /(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*([\d.]+)rem/gi;
+    while ((m = remRe.exec(content)) !== null) { const v = Math.round(parseFloat(m[1]) * 16); if (v > 0 && v < 200) vals.push(v); }
+    const gapRe = /gap\s*:\s*(\d+)px/gi;
+    while ((m = gapRe.exec(content)) !== null) vals.push(+m[1]);
+    const twRe = /\b(?:p|px|py|pt|pb|pl|pr|m|mx|my|mt|mb|ml|mr|gap)-(\d+)\b/g;
+    while ((m = twRe.exec(content)) !== null) vals.push(+m[1] * 4);
+    const rounded = vals.map(v => Math.round(v / 4) * 4);
+    if (rounded.length < 10) return [];
+    const counts = {};
+    for (const v of rounded) counts[v] = (counts[v] || 0) + 1;
+    const maxCount = Math.max(...Object.values(counts));
+    const pct = maxCount / rounded.length;
+    const unique = [...new Set(rounded)].filter(v => v > 0);
+    if (pct <= 0.6 || unique.length > 3) return [];
+    const dominant = Object.entries(counts).sort((a, b) => b[1] - a[1])[0][0];
+    return [finding('monotonous-spacing', filePath, `~${dominant}px used ${maxCount}/${rounded.length} times (${Math.round(pct * 100)}%)`)];
+  },
+  // Everything centered (regex)
+  (content, filePath) => {
+    const lines = content.split('\n');
+    let centered = 0, total = 0;
+    for (const line of lines) {
+      if (/<(?:h[1-6]|p|div|li|button)\b[^>]*>/i.test(line) && line.trim().length > 20) {
+        total++;
+        if (/text-align\s*:\s*center/i.test(line) || /\btext-center\b/.test(line)) centered++;
+      }
+    }
+    if (total < 5 || centered / total <= 0.7) return [];
+    return [finding('everything-centered', filePath, `${centered}/${total} text elements centered (${Math.round(centered / total * 100)}%)`)];
+  },
+  // Dark glow (page-level: dark bg + colored box-shadow with blur)
+  (content, filePath) => {
+    // Check if page has a dark background
+    const darkBgRe = /background(?:-color)?\s*:\s*(?:#(?:0[0-9a-f]|1[0-9a-f]|2[0-3])[0-9a-f]{4}\b|#(?:0|1)[0-9a-f]{2}\b|rgb\(\s*(\d{1,2})\s*,\s*(\d{1,2})\s*,\s*(\d{1,2})\s*\))/gi;
+    const twDarkBg = /\bbg-(?:gray|slate|zinc|neutral|stone)-(?:9\d{2}|800)\b/;
+    const hasDarkBg = darkBgRe.test(content) || twDarkBg.test(content);
+    if (!hasDarkBg) return [];
+
+    // Check for colored box-shadow with blur > 4px
+    const shadowRe = /box-shadow\s*:\s*([^;{}]+)/gi;
+    let m;
+    while ((m = shadowRe.exec(content)) !== null) {
+      const val = m[1];
+      const colorMatch = val.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
+      if (!colorMatch) continue;
+      const [r, g, b] = [+colorMatch[1], +colorMatch[2], +colorMatch[3]];
+      if ((Math.max(r, g, b) - Math.min(r, g, b)) < 30) continue; // skip gray
+      // Check blur: look for pattern like "0 0 20px" (third number > 4)
+      const pxVals = [...val.matchAll(/(\d+)px|(?<![.\d])\b(0)\b(?![.\d])/g)].map(p => +(p[1] || p[2]));
+      if (pxVals.length >= 3 && pxVals[2] > 4) {
+        const lines = content.substring(0, m.index).split('\n');
+        return [finding('dark-glow', filePath, `Colored glow (rgb(${r},${g},${b})) on dark page`, lines.length)];
+      }
+    }
+    return [];
+  },
+];
+
+// ---------------------------------------------------------------------------
+// Style block extraction (Vue/Svelte <style> blocks)
+// ---------------------------------------------------------------------------
+
+function extractStyleBlocks(content, ext) {
+  ext = ext.toLowerCase();
+  if (ext !== '.vue' && ext !== '.svelte') return [];
+  const blocks = [];
+  const re = /<style[^>]*>([\s\S]*?)<\/style>/gi;
+  let m;
+  while ((m = re.exec(content)) !== null) {
+    const before = content.substring(0, m.index);
+    const startLine = before.split('\n').length + 1;
+    blocks.push({ content: m[1], startLine });
+  }
+  return blocks;
+}
+
+// ---------------------------------------------------------------------------
+// CSS-in-JS extraction (styled-components, emotion)
+// ---------------------------------------------------------------------------
+
+const CSS_IN_JS_EXTENSIONS = new Set(['.js', '.ts', '.jsx', '.tsx']);
+
+function extractCSSinJS(content, ext) {
+  ext = ext.toLowerCase();
+  if (!CSS_IN_JS_EXTENSIONS.has(ext)) return [];
+  const blocks = [];
+  const re = /(?:styled(?:\.\w+|\([^)]+\))|css)\s*`([\s\S]*?)`/g;
+  let m;
+  while ((m = re.exec(content)) !== null) {
+    const before = content.substring(0, m.index);
+    const startLine = before.split('\n').length;
+    blocks.push({ content: m[1], startLine });
+  }
+  return blocks;
+}
+
+function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null) {
+  const findings = [];
+  for (const matcher of REGEX_MATCHERS) {
+    for (let i = 0; i < lines.length; i++) {
+      const line = lines[i];
+      matcher.regex.lastIndex = 0;
+      let m;
+      while ((m = matcher.regex.exec(line)) !== null) {
+        // For extracted blocks, use nearby lines as context for multi-line CSS patterns
+        const context = blockContext
+          ? lines.slice(Math.max(0, i - 3), Math.min(lines.length, i + 4)).join(' ')
+          : line;
+        if (matcher.test(m, context)) {
+          findings.push(finding(matcher.id, filePath, matcher.fmt(m, context), i + 1 + lineOffset));
+        }
+      }
+    }
+  }
+  return findings;
+}
+
+function detectText(content, filePath) {
+  const findings = [];
+  const lines = content.split('\n');
+  const ext = filePath ? (filePath.match(/\.\w+$/)?.[0] || '').toLowerCase() : '';
+
+  // Run regex matchers on the full file content (catches Tailwind classes, inline styles)
+  // Enable block context for CSS files where related properties span multiple lines
+  const cssLike = new Set(['.css', '.scss', '.less']);
+  findings.push(...runRegexMatchers(lines, filePath, 0, cssLike.has(ext) || null));
+
+  // Extract and scan <style> blocks from Vue/Svelte SFCs
+  const styleBlocks = extractStyleBlocks(content, ext);
+  for (const block of styleBlocks) {
+    const blockLines = block.content.split('\n');
+    findings.push(...runRegexMatchers(blockLines, filePath, block.startLine - 1, true));
+  }
+
+  // Extract and scan CSS-in-JS template literals
+  const cssJsBlocks = extractCSSinJS(content, ext);
+  for (const block of cssJsBlocks) {
+    const blockLines = block.content.split('\n');
+    findings.push(...runRegexMatchers(blockLines, filePath, block.startLine - 1, true));
+  }
+
+  // Deduplicate findings (same antipattern + similar snippet, within 2 lines)
+  const deduped = [];
+  for (const f of findings) {
+    const isDupe = deduped.some(d =>
+      d.antipattern === f.antipattern &&
+      d.snippet === f.snippet &&
+      Math.abs(d.line - f.line) <= 2
+    );
+    if (!isDupe) deduped.push(f);
+  }
+
+  // Page-level analyzers only run on full pages
+  if (isFullPage(content)) {
+    for (const analyzer of REGEX_ANALYZERS) {
+      deduped.push(...analyzer(content, filePath));
+    }
+  }
+
+  return deduped;
+}
+
+// ---------------------------------------------------------------------------
+// File walker
+// ---------------------------------------------------------------------------
+
+const SKIP_DIRS = new Set([
+  'node_modules', '.git', 'dist', 'build', '.next', '.nuxt', '.output',
+  '.svelte-kit', '__pycache__', '.turbo', '.vercel',
+]);
+
+const SCANNABLE_EXTENSIONS = new Set([
+  '.html', '.htm', '.css', '.scss', '.less',
+  '.jsx', '.tsx', '.js', '.ts',
+  '.vue', '.svelte', '.astro',
+]);
+
+const HTML_EXTENSIONS = new Set(['.html', '.htm']);
+
+function walkDir(dir) {
+  const files = [];
+  let entries;
+  try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return files; }
+  for (const entry of entries) {
+    if (SKIP_DIRS.has(entry.name)) continue;
+    const full = path.join(dir, entry.name);
+    if (entry.isDirectory()) files.push(...walkDir(full));
+    else if (SCANNABLE_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) files.push(full);
+  }
+  return files;
+}
+
+// ---------------------------------------------------------------------------
+// Output formatting
+// ---------------------------------------------------------------------------
+
+function formatFindings(findings, jsonMode) {
+  if (jsonMode) return JSON.stringify(findings, null, 2);
+
+  const grouped = {};
+  for (const f of findings) {
+    if (!grouped[f.file]) grouped[f.file] = [];
+    grouped[f.file].push(f);
+  }
+  const out = [];
+  for (const [file, items] of Object.entries(grouped)) {
+    const importNote = items[0]?.importedBy?.length ? ` (imported by ${items[0].importedBy.join(', ')})` : '';
+    out.push(`\n${file}${importNote}`);
+    for (const item of items) {
+      out.push(`  ${item.line ? `line ${item.line}: ` : ''}[${item.antipattern}] ${item.snippet}`);
+      out.push(`    → ${item.description}`);
+    }
+  }
+  out.push(`\n${findings.length} anti-pattern${findings.length === 1 ? '' : 's'} found.`);
+  return out.join('\n');
+}
+
+// ---------------------------------------------------------------------------
+// Stdin handling
+// ---------------------------------------------------------------------------
+
+async function handleStdin() {
+  const chunks = [];
+  for await (const chunk of process.stdin) chunks.push(chunk);
+  const input = Buffer.concat(chunks).toString('utf-8');
+  try {
+    const parsed = JSON.parse(input);
+    const fp = parsed?.tool_input?.file_path;
+    if (fp && fs.existsSync(fp)) {
+      return HTML_EXTENSIONS.has(path.extname(fp).toLowerCase())
+        ? detectHtml(fp) : detectText(fs.readFileSync(fp, 'utf-8'), fp);
+    }
+  } catch { /* not JSON */ }
+  return detectText(input, '<stdin>');
+}
+
+// ---------------------------------------------------------------------------
+// Import graph (multi-file awareness)
+// ---------------------------------------------------------------------------
+
+function resolveImport(specifier, fromDir, fileSet) {
+  if (!/^[./]/.test(specifier)) return null; // skip bare specifiers
+  const base = path.resolve(fromDir, specifier);
+  if (fileSet.has(base)) return base;
+  for (const ext of SCANNABLE_EXTENSIONS) {
+    const withExt = base + ext;
+    if (fileSet.has(withExt)) return withExt;
+  }
+  // index file convention
+  for (const ext of SCANNABLE_EXTENSIONS) {
+    const indexFile = path.join(base, 'index' + ext);
+    if (fileSet.has(indexFile)) return indexFile;
+  }
+  return null;
+}
+
+function buildImportGraph(files) {
+  const fileSet = new Set(files);
+  const graph = new Map();
+
+  for (const file of files) {
+    const content = fs.readFileSync(file, 'utf-8');
+    const dir = path.dirname(file);
+    const imports = new Set();
+
+    // ES imports: import ... from '...' and import '...'
+    const esRe = /import\s+(?:[\s\S]*?from\s+)?['"]([^'"]+)['"]/g;
+    let m;
+    while ((m = esRe.exec(content)) !== null) {
+      const resolved = resolveImport(m[1], dir, fileSet);
+      if (resolved) imports.add(resolved);
+    }
+
+    // CSS @import
+    const cssRe = /@import\s+(?:url\(\s*)?['"]?([^'");\s]+)['"]?\s*\)?/g;
+    while ((m = cssRe.exec(content)) !== null) {
+      const resolved = resolveImport(m[1], dir, fileSet);
+      if (resolved) imports.add(resolved);
+    }
+
+    // SCSS @use / @forward
+    const scssRe = /@(?:use|forward)\s+['"]([^'"]+)['"]/g;
+    while ((m = scssRe.exec(content)) !== null) {
+      const resolved = resolveImport(m[1], dir, fileSet);
+      if (resolved) imports.add(resolved);
+    }
+
+    graph.set(file, imports);
+  }
+  return graph;
+}
+
+// ---------------------------------------------------------------------------
+// Framework dev server detection
+// ---------------------------------------------------------------------------
+
+const FRAMEWORK_CONFIGS = [
+  { name: 'Next.js', files: ['next.config.js', 'next.config.mjs', 'next.config.ts'], defaultPort: 3000,
+    portRe: /port\s*[:=]\s*(\d+)/,
+    fingerprint: { header: 'x-powered-by', value: /next/i } },
+  { name: 'SvelteKit', files: ['svelte.config.js', 'svelte.config.ts'], defaultPort: 5173,
+    portRe: /port\s*[:=]\s*(\d+)/,
+    fingerprint: { header: 'x-sveltekit-page', value: null } },
+  { name: 'Nuxt', files: ['nuxt.config.js', 'nuxt.config.ts'], defaultPort: 3000,
+    portRe: /port\s*[:=]\s*(\d+)/,
+    fingerprint: { header: 'x-powered-by', value: /nuxt/i } },
+  { name: 'Vite', files: ['vite.config.js', 'vite.config.ts', 'vite.config.mjs'], defaultPort: 5173,
+    portRe: /port\s*[:=]\s*(\d+)/,
+    fingerprint: { body: /@vite\/client/ } },
+  { name: 'Astro', files: ['astro.config.js', 'astro.config.ts', 'astro.config.mjs'], defaultPort: 4321,
+    portRe: /port\s*[:=]\s*(\d+)/,
+    fingerprint: { body: /astro/i } },
+  { name: 'Angular', files: ['angular.json'], defaultPort: 4200,
+    portRe: /"port"\s*:\s*(\d+)/,
+    fingerprint: { body: /ng-version/i } },
+  { name: 'Remix', files: ['remix.config.js', 'remix.config.ts'], defaultPort: 3000,
+    portRe: /port\s*[:=]\s*(\d+)/,
+    fingerprint: { header: 'x-powered-by', value: /remix/i } },
+];
+
+function detectFrameworkConfig(dir) {
+  let entries;
+  try { entries = fs.readdirSync(dir); } catch { return null; }
+  const entrySet = new Set(entries);
+
+  for (const cfg of FRAMEWORK_CONFIGS) {
+    const match = cfg.files.find(f => entrySet.has(f));
+    if (!match) continue;
+
+    const configPath = path.join(dir, match);
+    let port = cfg.defaultPort;
+    try {
+      const content = fs.readFileSync(configPath, 'utf-8');
+      const portMatch = content.match(cfg.portRe);
+      if (portMatch) port = parseInt(portMatch[1], 10);
+    } catch { /* use default */ }
+
+    return { name: cfg.name, port, configPath, fingerprint: cfg.fingerprint };
+  }
+  return null;
+}
+
+/**
+ * Check if a port is listening and optionally verify it matches the expected framework.
+ * Returns { listening: true, matched: true/false } or { listening: false }.
+ */
+async function isPortListening(port, fingerprint = null) {
+  if (!fingerprint) {
+    // Simple TCP probe fallback
+    const net = await import('node:net');
+    return new Promise((resolve) => {
+      const sock = net.default.createConnection({ port, host: '127.0.0.1' });
+      sock.setTimeout(500);
+      sock.on('connect', () => { sock.destroy(); resolve({ listening: true, matched: true }); });
+      sock.on('error', () => resolve({ listening: false }));
+      sock.on('timeout', () => { sock.destroy(); resolve({ listening: false }); });
+    });
+  }
+
+  // HTTP probe with fingerprint matching
+  try {
+    const controller = new AbortController();
+    const timeout = setTimeout(() => controller.abort(), 2000);
+    const res = await fetch(`http://localhost:${port}/`, { signal: controller.signal, redirect: 'follow' });
+    clearTimeout(timeout);
+
+    // Check header fingerprint
+    if (fingerprint.header) {
+      const val = res.headers.get(fingerprint.header);
+      if (val && (!fingerprint.value || fingerprint.value.test(val))) {
+        return { listening: true, matched: true };
+      }
+    }
+
+    // Check body fingerprint
+    if (fingerprint.body) {
+      const body = await res.text();
+      if (fingerprint.body.test(body)) {
+        return { listening: true, matched: true };
+      }
+    }
+
+    // Port is listening but doesn't match the expected framework
+    return { listening: true, matched: false };
+  } catch {
+    return { listening: false };
+  }
+}
+
+// ---------------------------------------------------------------------------
+// CLI
+// ---------------------------------------------------------------------------
+
+async function confirm(question) {
+  const rl = (await import('node:readline')).default.createInterface({
+    input: process.stdin, output: process.stderr,
+  });
+  return new Promise((resolve) => {
+    rl.question(`${question} [Y/n] `, (answer) => {
+      rl.close();
+      resolve(!answer || /^y(es)?$/i.test(answer.trim()));
+    });
+  });
+}
+
+function printUsage() {
+  console.log(`Usage: impeccable detect [options] [file-or-dir-or-url...]
+
+Scan files or URLs for UI anti-patterns and design quality issues.
+
+Options:
+  --fast    Regex-only mode (skip jsdom, faster but misses linked stylesheets)
+  --json    Output results as JSON
+  --help    Show this help message
+
+Detection modes:
+  HTML files     jsdom with computed styles (default, catches linked CSS)
+  Non-HTML files Regex pattern matching (CSS, JSX, TSX, etc.)
+  URLs           Puppeteer full browser rendering (auto-detected)
+  --fast         Forces regex for all files
+
+Examples:
+  impeccable detect src/
+  impeccable detect index.html
+  impeccable detect https://example.com
+  impeccable detect --fast --json .`);
+}
+
+async function main() {
+  const args = process.argv.slice(2);
+  const jsonMode = args.includes('--json');
+  const helpMode = args.includes('--help');
+  const fastMode = args.includes('--fast');
+  const targets = args.filter(a => !a.startsWith('--'));
+
+  if (helpMode) { printUsage(); process.exit(0); }
+
+  let allFindings = [];
+
+  if (!process.stdin.isTTY && targets.length === 0) {
+    allFindings = await handleStdin();
+  } else {
+    const paths = targets.length > 0 ? targets : [process.cwd()];
+
+    for (const target of paths) {
+      if (/^https?:\/\//i.test(target)) {
+        try { allFindings.push(...await detectUrl(target)); }
+        catch (e) { process.stderr.write(`Error: ${e.message}\n`); }
+        continue;
+      }
+
+      const resolved = path.resolve(target);
+      let stat;
+      try { stat = fs.statSync(resolved); }
+      catch { process.stderr.write(`Warning: cannot access ${target}\n`); continue; }
+
+      if (stat.isDirectory()) {
+        // Check for framework dev server config (skip in JSON mode to avoid polluting output)
+        if (!jsonMode) {
+          const fwConfig = detectFrameworkConfig(resolved);
+          if (fwConfig) {
+            const probe = await isPortListening(fwConfig.port, fwConfig.fingerprint);
+            if (probe.listening && probe.matched) {
+              process.stderr.write(
+                `\n${fwConfig.name} dev server detected on localhost:${fwConfig.port}.\n` +
+                `For more accurate results, scan the running site:\n` +
+                `  npx impeccable detect http://localhost:${fwConfig.port}\n\n`
+              );
+            } else if (probe.listening && !probe.matched) {
+              process.stderr.write(
+                `\n${fwConfig.name} project detected (${path.basename(fwConfig.configPath)}).\n` +
+                `Port ${fwConfig.port} is in use by another service. Start the ${fwConfig.name} dev server and scan via URL for best results.\n\n`
+              );
+            } else {
+              process.stderr.write(
+                `\n${fwConfig.name} project detected (${path.basename(fwConfig.configPath)}).\n` +
+                `Start the dev server and scan via URL for best results:\n` +
+                `  npx impeccable detect http://localhost:${fwConfig.port}\n\n`
+              );
+            }
+          }
+        }
+
+        const files = walkDir(resolved);
+        const htmlCount = files.filter(f => HTML_EXTENSIONS.has(path.extname(f).toLowerCase())).length;
+
+        // Warn and confirm if scanning many files (jsdom is slow per HTML file)
+        if (files.length > 50 && process.stdin.isTTY && !jsonMode) {
+          process.stderr.write(
+            `\nFound ${files.length} files (${htmlCount} HTML) in ${target}.\n` +
+            `Scanning may take a while${htmlCount > 10 ? ' (jsdom processes each HTML file individually)' : ''}.\n` +
+            `Use --fast to skip jsdom, or target a specific subdirectory.\n`
+          );
+          const ok = await confirm('Continue?');
+          if (!ok) { process.stderr.write('Aborted.\n'); process.exit(0); }
+        }
+
+        // Build import graph for multi-file awareness
+        const graph = buildImportGraph(files);
+        // Build reverse map: file -> set of files that import it
+        const importedByMap = new Map();
+        for (const [importer, imports] of graph) {
+          for (const imported of imports) {
+            if (!importedByMap.has(imported)) importedByMap.set(imported, new Set());
+            importedByMap.get(imported).add(importer);
+          }
+        }
+
+        for (const file of files) {
+          const ext = path.extname(file).toLowerCase();
+          let fileFindings;
+          if (!fastMode && HTML_EXTENSIONS.has(ext)) {
+            fileFindings = await detectHtml(file);
+          } else {
+            fileFindings = detectText(fs.readFileSync(file, 'utf-8'), file);
+          }
+          // Annotate findings with import context
+          const importers = importedByMap.get(file);
+          if (importers && importers.size > 0) {
+            const importerNames = [...importers].map(f => path.basename(f));
+            for (const f of fileFindings) {
+              f.importedBy = importerNames;
+            }
+          }
+          allFindings.push(...fileFindings);
+        }
+      } else if (stat.isFile()) {
+        const ext = path.extname(resolved).toLowerCase();
+        if (!fastMode && HTML_EXTENSIONS.has(ext)) {
+          allFindings.push(...await detectHtml(resolved));
+        } else {
+          allFindings.push(...detectText(fs.readFileSync(resolved, 'utf-8'), resolved));
+        }
+      }
+    }
+  }
+
+  if (allFindings.length > 0) {
+    process.stderr.write(formatFindings(allFindings, jsonMode) + '\n');
+    process.exit(2);
+  }
+  if (jsonMode) process.stdout.write('[]\n');
+  process.exit(0);
+}
+
+// ---------------------------------------------------------------------------
+// Live detection server
+// ---------------------------------------------------------------------------
+
+async function findOpenPort(start = 8400) {
+  const net = await import('node:net');
+  return new Promise((resolve) => {
+    const server = net.default.createServer();
+    server.listen(start, '127.0.0.1', () => {
+      const port = server.address().port;
+      server.close(() => resolve(port));
+    });
+    server.on('error', () => resolve(findOpenPort(start + 1)));
+  });
+}
+
+const LIVE_PID_FILE = path.join((await import('node:os')).default.tmpdir(), 'impeccable-live.json');
+
+async function liveCli() {
+  const args = process.argv.slice(2);
+  const helpMode = args.includes('--help');
+  const stopMode = args.includes('stop');
+  const portArg = args.find(a => a.startsWith('--port='));
+  const requestedPort = portArg ? parseInt(portArg.split('=')[1], 10) : null;
+
+  if (helpMode) {
+    console.log(`Usage: impeccable live [options]
+
+Start a local server that serves the browser detection overlay script.
+Inject the script into any page to scan for anti-patterns in real time.
+
+Commands:
+  live          Start the server (default)
+  live stop     Stop a running live server
+
+Options:
+  --port=PORT   Use a specific port (default: auto-detect unused port)
+  --help        Show this help message
+
+The server provides:
+  /detect.js    The detection overlay script (inject via <script> tag)
+  /health       Health check endpoint
+  /stop         Stop the server remotely`);
+    process.exit(0);
+  }
+
+  // Stop a running server
+  if (stopMode) {
+    try {
+      const info = JSON.parse(fs.readFileSync(LIVE_PID_FILE, 'utf-8'));
+      const res = await fetch(`http://localhost:${info.port}/stop`);
+      if (res.ok) {
+        console.log(`Stopped live server on port ${info.port}.`);
+      }
+    } catch {
+      console.log('No running live server found.');
+    }
+    process.exit(0);
+  }
+
+  const http = await import('node:http');
+  const scriptPath = path.join(path.dirname(new URL(import.meta.url).pathname), 'detect-antipatterns-browser.js');
+
+  let browserScript;
+  try {
+    browserScript = fs.readFileSync(scriptPath, 'utf-8');
+  } catch {
+    process.stderr.write('Error: Browser script not found. Run `npm run build:browser` first.\n');
+    process.exit(1);
+  }
+
+  const port = requestedPort || await findOpenPort();
+
+  const shutdown = () => {
+    try { fs.unlinkSync(LIVE_PID_FILE); } catch { /* ignore */ }
+    server.close();
+    process.exit(0);
+  };
+
+  const server = http.default.createServer((req, res) => {
+    // CORS headers for cross-origin injection
+    res.setHeader('Access-Control-Allow-Origin', '*');
+    res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
+    if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
+
+    if (req.url === '/detect.js' || req.url === '/') {
+      res.writeHead(200, { 'Content-Type': 'application/javascript' });
+      res.end(browserScript);
+    } else if (req.url === '/health') {
+      res.writeHead(200, { 'Content-Type': 'application/json' });
+      res.end(JSON.stringify({ status: 'ok', port }));
+    } else if (req.url === '/stop') {
+      res.writeHead(200, { 'Content-Type': 'text/plain' });
+      res.end('stopping');
+      shutdown();
+    } else {
+      res.writeHead(404);
+      res.end('Not found');
+    }
+  });
+
+  server.listen(port, '127.0.0.1', () => {
+    // Write PID file so `live stop` can find us
+    fs.writeFileSync(LIVE_PID_FILE, JSON.stringify({ pid: process.pid, port }));
+
+    const url = `http://localhost:${port}`;
+    console.log(`Impeccable live detection server running on ${url}\n`);
+    console.log(`Inject into any page:`);
+    console.log(`  const s = document.createElement('script');`);
+    console.log(`  s.src = '${url}/detect.js';`);
+    console.log(`  document.head.appendChild(s);\n`);
+    console.log(`Stop: npx impeccable live stop`);
+  });
+
+  process.on('SIGINT', shutdown);
+  process.on('SIGTERM', shutdown);
+}
+
+// ---------------------------------------------------------------------------
+// Entry point
+// ---------------------------------------------------------------------------
+
+if (!IS_BROWSER) {
+  const isMainModule = process.argv[1]?.endsWith('detect-antipatterns.mjs') ||
+    process.argv[1]?.endsWith('detect-antipatterns.mjs/');
+  if (isMainModule) main();
+}
+
+// @browser-strip-end
+
+// ─── Section 9: Exports ─────────────────────────────────────────────────────
+// @browser-strip-start
+
+export {
+  ANTIPATTERNS, SAFE_TAGS, OVERUSED_FONTS, GENERIC_FONTS,
+  checkElementBorders, checkElementMotion, checkElementGlow, checkPageTypography, checkPageLayout, isNeutralColor, isFullPage,
+  detectHtml, detectUrl, detectText,
+  walkDir, formatFindings, SCANNABLE_EXTENSIONS, SKIP_DIRS,
+  extractStyleBlocks, extractCSSinJS,
+  buildImportGraph, resolveImport,
+  detectFrameworkConfig, isPortListening, FRAMEWORK_CONFIGS,
+  main as detectCli,
+  liveCli,
+};
+
+// @browser-strip-end

tests/build.test.js 🔗

@@ -0,0 +1,297 @@
+import { describe, test, expect, beforeEach, afterEach, spyOn } from 'bun:test';
+import fs from 'fs';
+import path from 'path';
+import * as utils from '../scripts/lib/utils.js';
+import * as transformers from '../scripts/lib/transformers/index.js';
+
+const TEST_DIR = path.join(process.cwd(), 'test-tmp-build');
+
+describe('build orchestration', () => {
+  beforeEach(() => {
+    if (fs.existsSync(TEST_DIR)) {
+      fs.rmSync(TEST_DIR, { recursive: true, force: true });
+    }
+    fs.mkdirSync(TEST_DIR, { recursive: true });
+  });
+
+  afterEach(() => {
+    if (fs.existsSync(TEST_DIR)) {
+      fs.rmSync(TEST_DIR, { recursive: true, force: true });
+    }
+  });
+
+  test('should call readSourceFiles with root directory', () => {
+    const readSourceFilesSpy = spyOn(utils, 'readSourceFiles').mockReturnValue({
+      skills: []
+    });
+
+    const transformCursorSpy = spyOn(transformers, 'transformCursor').mockImplementation(() => {});
+    const transformClaudeCodeSpy = spyOn(transformers, 'transformClaudeCode').mockImplementation(() => {});
+    const transformGeminiSpy = spyOn(transformers, 'transformGemini').mockImplementation(() => {});
+    const transformCodexSpy = spyOn(transformers, 'transformCodex').mockImplementation(() => {});
+
+    // Simulate the build process
+    const ROOT_DIR = TEST_DIR;
+    const DIST_DIR = path.join(ROOT_DIR, 'dist');
+
+    const { skills } = utils.readSourceFiles(ROOT_DIR);
+    const patterns = utils.readPatterns(ROOT_DIR);
+    transformers.transformCursor(skills, DIST_DIR, patterns);
+    transformers.transformClaudeCode(skills, DIST_DIR, patterns);
+    transformers.transformGemini(skills, DIST_DIR, patterns);
+    transformers.transformCodex(skills, DIST_DIR, patterns);
+
+    expect(readSourceFilesSpy).toHaveBeenCalledWith(ROOT_DIR);
+
+    readSourceFilesSpy.mockRestore();
+    transformCursorSpy.mockRestore();
+    transformClaudeCodeSpy.mockRestore();
+    transformGeminiSpy.mockRestore();
+    transformCodexSpy.mockRestore();
+  });
+
+  test('should call all transformers with correct arguments', () => {
+    const skills = [
+      { name: 'skill1', description: 'Skill 1', license: 'MIT', body: 'Skill body 1' }
+    ];
+    const patterns = { patterns: [], antipatterns: [] };
+
+    const readSourceFilesSpy = spyOn(utils, 'readSourceFiles').mockReturnValue({
+      skills
+    });
+    const readPatternsSpy = spyOn(utils, 'readPatterns').mockReturnValue(patterns);
+
+    const transformCursorSpy = spyOn(transformers, 'transformCursor').mockImplementation(() => {});
+    const transformClaudeCodeSpy = spyOn(transformers, 'transformClaudeCode').mockImplementation(() => {});
+    const transformGeminiSpy = spyOn(transformers, 'transformGemini').mockImplementation(() => {});
+    const transformCodexSpy = spyOn(transformers, 'transformCodex').mockImplementation(() => {});
+
+    const ROOT_DIR = TEST_DIR;
+    const DIST_DIR = path.join(ROOT_DIR, 'dist');
+
+    const sourceFiles = utils.readSourceFiles(ROOT_DIR);
+    const patternData = utils.readPatterns(ROOT_DIR);
+    transformers.transformCursor(sourceFiles.skills, DIST_DIR, patternData);
+    transformers.transformClaudeCode(sourceFiles.skills, DIST_DIR, patternData);
+    transformers.transformGemini(sourceFiles.skills, DIST_DIR, patternData);
+    transformers.transformCodex(sourceFiles.skills, DIST_DIR, patternData);
+
+    expect(transformCursorSpy).toHaveBeenCalledWith(skills, DIST_DIR, patterns);
+    expect(transformClaudeCodeSpy).toHaveBeenCalledWith(skills, DIST_DIR, patterns);
+    expect(transformGeminiSpy).toHaveBeenCalledWith(skills, DIST_DIR, patterns);
+    expect(transformCodexSpy).toHaveBeenCalledWith(skills, DIST_DIR, patterns);
+
+    readSourceFilesSpy.mockRestore();
+    readPatternsSpy.mockRestore();
+    transformCursorSpy.mockRestore();
+    transformClaudeCodeSpy.mockRestore();
+    transformGeminiSpy.mockRestore();
+    transformCodexSpy.mockRestore();
+  });
+
+  test('should handle empty source files', () => {
+    const patterns = { patterns: [], antipatterns: [] };
+
+    const readSourceFilesSpy = spyOn(utils, 'readSourceFiles').mockReturnValue({
+      skills: []
+    });
+    const readPatternsSpy = spyOn(utils, 'readPatterns').mockReturnValue(patterns);
+
+    const transformCursorSpy = spyOn(transformers, 'transformCursor').mockImplementation(() => {});
+    const transformClaudeCodeSpy = spyOn(transformers, 'transformClaudeCode').mockImplementation(() => {});
+    const transformGeminiSpy = spyOn(transformers, 'transformGemini').mockImplementation(() => {});
+    const transformCodexSpy = spyOn(transformers, 'transformCodex').mockImplementation(() => {});
+
+    const ROOT_DIR = TEST_DIR;
+    const DIST_DIR = path.join(ROOT_DIR, 'dist');
+
+    const { skills } = utils.readSourceFiles(ROOT_DIR);
+    const patternData = utils.readPatterns(ROOT_DIR);
+    transformers.transformCursor(skills, DIST_DIR, patternData);
+    transformers.transformClaudeCode(skills, DIST_DIR, patternData);
+    transformers.transformGemini(skills, DIST_DIR, patternData);
+    transformers.transformCodex(skills, DIST_DIR, patternData);
+
+    expect(transformCursorSpy).toHaveBeenCalledWith([], DIST_DIR, patterns);
+    expect(transformClaudeCodeSpy).toHaveBeenCalledWith([], DIST_DIR, patterns);
+    expect(transformGeminiSpy).toHaveBeenCalledWith([], DIST_DIR, patterns);
+    expect(transformCodexSpy).toHaveBeenCalledWith([], DIST_DIR, patterns);
+
+    readSourceFilesSpy.mockRestore();
+    readPatternsSpy.mockRestore();
+    transformCursorSpy.mockRestore();
+    transformClaudeCodeSpy.mockRestore();
+    transformGeminiSpy.mockRestore();
+    transformCodexSpy.mockRestore();
+  });
+
+  test('integration: full build creates all expected outputs', () => {
+    // Create test source files
+    const skillContent = `---
+name: test-skill
+description: A test skill
+license: MIT
+---
+
+This is a test skill body.`;
+
+    const skillDir = path.join(TEST_DIR, 'source/skills/test-skill');
+    fs.mkdirSync(skillDir, { recursive: true });
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
+
+    // Run the build process
+    const DIST_DIR = path.join(TEST_DIR, 'dist');
+    const { skills } = utils.readSourceFiles(TEST_DIR);
+    const patterns = utils.readPatterns(TEST_DIR);
+
+    transformers.transformCursor(skills, DIST_DIR, patterns);
+    transformers.transformClaudeCode(skills, DIST_DIR, patterns);
+    transformers.transformGemini(skills, DIST_DIR, patterns);
+    transformers.transformCodex(skills, DIST_DIR, patterns);
+
+    // Verify Cursor outputs
+    expect(fs.existsSync(path.join(DIST_DIR, 'cursor/.cursor/skills/test-skill/SKILL.md'))).toBe(true);
+
+    // Verify Claude Code outputs
+    expect(fs.existsSync(path.join(DIST_DIR, 'claude-code/.claude/skills/test-skill/SKILL.md'))).toBe(true);
+
+    // Verify Gemini outputs
+    expect(fs.existsSync(path.join(DIST_DIR, 'gemini/.gemini/skills/test-skill/SKILL.md'))).toBe(true);
+
+    // Verify Codex outputs
+    expect(fs.existsSync(path.join(DIST_DIR, 'codex/.codex/skills/test-skill/SKILL.md'))).toBe(true);
+  });
+
+  test('integration: verify transformations are correct', () => {
+    const skillContent = `---
+name: audit
+description: Run technical quality checks
+user-invocable: true
+argument-hint: "[TARGET=<value>]"
+---
+
+Please audit {{target}} for technical quality. Ask {{model}} for help.`;
+
+    const skillDir = path.join(TEST_DIR, 'source/skills/audit');
+    fs.mkdirSync(skillDir, { recursive: true });
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
+
+    const DIST_DIR = path.join(TEST_DIR, 'dist');
+    const { skills } = utils.readSourceFiles(TEST_DIR);
+    const patterns = utils.readPatterns(TEST_DIR);
+
+    transformers.transformCursor(skills, DIST_DIR, patterns);
+    transformers.transformClaudeCode(skills, DIST_DIR, patterns);
+    transformers.transformGemini(skills, DIST_DIR, patterns);
+    transformers.transformCodex(skills, DIST_DIR, patterns);
+
+    // Verify Cursor: full frontmatter with user-invocable
+    const cursorContent = fs.readFileSync(path.join(DIST_DIR, 'cursor/.cursor/skills/audit/SKILL.md'), 'utf-8');
+    expect(cursorContent).toContain('---');
+    expect(cursorContent).toContain('name: audit');
+    expect(cursorContent).toContain('{{target}}');
+    expect(cursorContent).toContain('the model');
+
+    // Verify Claude Code: full frontmatter with user-invocable and argument-hint
+    const claudeContent = fs.readFileSync(path.join(DIST_DIR, 'claude-code/.claude/skills/audit/SKILL.md'), 'utf-8');
+    expect(claudeContent).toContain('---');
+    expect(claudeContent).toContain('name: audit');
+    expect(claudeContent).toContain('user-invocable: true');
+    expect(claudeContent).toContain('{{target}}');
+    expect(claudeContent).toContain('Claude');
+
+    // Verify Gemini: skill in skills directory
+    expect(fs.existsSync(path.join(DIST_DIR, 'gemini/.gemini/skills/audit/SKILL.md'))).toBe(true);
+    const geminiContent = fs.readFileSync(path.join(DIST_DIR, 'gemini/.gemini/skills/audit/SKILL.md'), 'utf-8');
+    expect(geminiContent).toContain('{{target}}'); // No body transform, placeholder preserved
+    expect(geminiContent).toContain('Gemini');
+
+    // Verify Codex: skill in skills directory
+    expect(fs.existsSync(path.join(DIST_DIR, 'codex/.codex/skills/audit/SKILL.md'))).toBe(true);
+    const codexContent = fs.readFileSync(path.join(DIST_DIR, 'codex/.codex/skills/audit/SKILL.md'), 'utf-8');
+    expect(codexContent).toContain('{{target}}'); // No body transform, placeholder preserved
+    expect(codexContent).toContain('GPT');
+  });
+
+  test('integration: multiple skills', () => {
+    const skill1Dir = path.join(TEST_DIR, 'source/skills/skill1');
+    fs.mkdirSync(skill1Dir, { recursive: true });
+    fs.writeFileSync(path.join(skill1Dir, 'SKILL.md'), '---\nname: skill1\n---\nSkill1');
+
+    const skill2Dir = path.join(TEST_DIR, 'source/skills/skill2');
+    fs.mkdirSync(skill2Dir, { recursive: true });
+    fs.writeFileSync(path.join(skill2Dir, 'SKILL.md'), '---\nname: skill2\n---\nSkill2');
+
+    const DIST_DIR = path.join(TEST_DIR, 'dist');
+    const { skills } = utils.readSourceFiles(TEST_DIR);
+    const patterns = utils.readPatterns(TEST_DIR);
+
+    expect(skills).toHaveLength(2);
+
+    transformers.transformCursor(skills, DIST_DIR, patterns);
+    transformers.transformClaudeCode(skills, DIST_DIR, patterns);
+    transformers.transformGemini(skills, DIST_DIR, patterns);
+    transformers.transformCodex(skills, DIST_DIR, patterns);
+
+    // Verify all files exist
+    expect(fs.existsSync(path.join(DIST_DIR, 'cursor/.cursor/skills/skill1/SKILL.md'))).toBe(true);
+    expect(fs.existsSync(path.join(DIST_DIR, 'cursor/.cursor/skills/skill2/SKILL.md'))).toBe(true);
+    expect(fs.existsSync(path.join(DIST_DIR, 'claude-code/.claude/skills/skill1/SKILL.md'))).toBe(true);
+    expect(fs.existsSync(path.join(DIST_DIR, 'claude-code/.claude/skills/skill2/SKILL.md'))).toBe(true);
+  });
+
+  test('should call transformers in correct order', () => {
+    const callOrder = [];
+
+    const readSourceFilesSpy = spyOn(utils, 'readSourceFiles').mockReturnValue({
+      skills: []
+    });
+    const readPatternsSpy = spyOn(utils, 'readPatterns').mockReturnValue({ patterns: [], antipatterns: [] });
+
+    const transformCursorSpy = spyOn(transformers, 'transformCursor').mockImplementation(() => {
+      callOrder.push('cursor');
+    });
+    const transformClaudeCodeSpy = spyOn(transformers, 'transformClaudeCode').mockImplementation(() => {
+      callOrder.push('claude-code');
+    });
+    const transformGeminiSpy = spyOn(transformers, 'transformGemini').mockImplementation(() => {
+      callOrder.push('gemini');
+    });
+    const transformCodexSpy = spyOn(transformers, 'transformCodex').mockImplementation(() => {
+      callOrder.push('codex');
+    });
+
+    const ROOT_DIR = TEST_DIR;
+    const DIST_DIR = path.join(ROOT_DIR, 'dist');
+
+    const { skills } = utils.readSourceFiles(ROOT_DIR);
+    const patterns = utils.readPatterns(ROOT_DIR);
+    transformers.transformCursor(skills, DIST_DIR, patterns);
+    transformers.transformClaudeCode(skills, DIST_DIR, patterns);
+    transformers.transformGemini(skills, DIST_DIR, patterns);
+    transformers.transformCodex(skills, DIST_DIR, patterns);
+
+    expect(callOrder).toEqual(['cursor', 'claude-code', 'gemini', 'codex']);
+
+    readSourceFilesSpy.mockRestore();
+    readPatternsSpy.mockRestore();
+    transformCursorSpy.mockRestore();
+    transformClaudeCodeSpy.mockRestore();
+    transformGeminiSpy.mockRestore();
+    transformCodexSpy.mockRestore();
+  });
+
+  test('should include agents and kiro transformers', () => {
+    const { skills } = utils.readSourceFiles(TEST_DIR);
+    const patterns = utils.readPatterns(TEST_DIR);
+    const DIST_DIR = path.join(TEST_DIR, 'dist');
+
+    // These should not throw
+    transformers.transformAgents(skills, DIST_DIR, patterns);
+    transformers.transformKiro(skills, DIST_DIR, patterns);
+
+    // Verify outputs
+    expect(fs.existsSync(path.join(DIST_DIR, 'agents/.agents/skills'))).toBe(true);
+    expect(fs.existsSync(path.join(DIST_DIR, 'kiro/.kiro/skills'))).toBe(true);
+  });
+});

tests/cleanup-deprecated.test.mjs 🔗

@@ -0,0 +1,236 @@
+import { describe, it, beforeEach, afterEach } from 'node:test';
+import assert from 'node:assert/strict';
+import { mkdirSync, writeFileSync, readFileSync, existsSync, symlinkSync, rmSync } from 'node:fs';
+import { join } from 'node:path';
+import { mkdtempSync } from 'node:fs';
+import { tmpdir } from 'node:os';
+
+import {
+  findProjectRoot,
+  isImpeccableSkill,
+  buildTargetNames,
+  findSkillsDirs,
+  removeDeprecatedSkills,
+  cleanSkillsLock,
+  cleanup,
+} from '../source/skills/impeccable/scripts/cleanup-deprecated.mjs';
+
+function makeTmpDir() {
+  return mkdtempSync(join(tmpdir(), 'impeccable-cleanup-test-'));
+}
+
+function writeSkill(root, harness, name, content) {
+  const dir = join(root, harness, 'skills', name);
+  mkdirSync(dir, { recursive: true });
+  writeFileSync(join(dir, 'SKILL.md'), content, 'utf-8');
+  return dir;
+}
+
+describe('cleanup-deprecated', () => {
+  let tmp;
+
+  beforeEach(() => {
+    tmp = makeTmpDir();
+    // Mark as project root
+    writeFileSync(join(tmp, 'package.json'), '{}', 'utf-8');
+  });
+
+  afterEach(() => {
+    rmSync(tmp, { recursive: true, force: true });
+  });
+
+  describe('findProjectRoot', () => {
+    it('finds directory with package.json', () => {
+      const sub = join(tmp, 'a', 'b', 'c');
+      mkdirSync(sub, { recursive: true });
+      assert.equal(findProjectRoot(sub), tmp);
+    });
+
+    it('finds directory with skills-lock.json', () => {
+      const root2 = makeTmpDir();
+      writeFileSync(join(root2, 'skills-lock.json'), '{}', 'utf-8');
+      assert.equal(findProjectRoot(root2), root2);
+      rmSync(root2, { recursive: true, force: true });
+    });
+  });
+
+  describe('isImpeccableSkill', () => {
+    it('returns true when SKILL.md mentions impeccable', () => {
+      const dir = writeSkill(tmp, '.claude', 'arrange', 'Invoke /impeccable first.');
+      assert.equal(isImpeccableSkill(dir), true);
+    });
+
+    it('returns false when SKILL.md does not mention impeccable', () => {
+      const dir = writeSkill(tmp, '.claude', 'arrange', 'This is my custom arrange skill.');
+      assert.equal(isImpeccableSkill(dir), false);
+    });
+
+    it('returns false for non-existent directory', () => {
+      assert.equal(isImpeccableSkill(join(tmp, 'nope')), false);
+    });
+  });
+
+  describe('buildTargetNames', () => {
+    it('includes both unprefixed and i-prefixed names', () => {
+      const names = buildTargetNames();
+      assert.ok(names.includes('arrange'));
+      assert.ok(names.includes('i-arrange'));
+      assert.ok(names.includes('frontend-design'));
+      assert.ok(names.includes('i-frontend-design'));
+      assert.equal(names.length, 12); // 6 deprecated * 2
+    });
+  });
+
+  describe('findSkillsDirs', () => {
+    it('finds existing harness skill directories', () => {
+      mkdirSync(join(tmp, '.claude', 'skills'), { recursive: true });
+      mkdirSync(join(tmp, '.agents', 'skills'), { recursive: true });
+      const dirs = findSkillsDirs(tmp);
+      assert.equal(dirs.length, 2);
+    });
+
+    it('ignores non-existent harness directories', () => {
+      const dirs = findSkillsDirs(tmp);
+      assert.equal(dirs.length, 0);
+    });
+  });
+
+  describe('removeDeprecatedSkills', () => {
+    it('deletes impeccable-owned deprecated skill directories', () => {
+      writeSkill(tmp, '.claude', 'arrange', 'Invoke /impeccable first.');
+      writeSkill(tmp, '.claude', 'normalize', 'Run impeccable teach.');
+      const deleted = removeDeprecatedSkills(tmp);
+      assert.equal(deleted.length, 2);
+      assert.equal(existsSync(join(tmp, '.claude', 'skills', 'arrange')), false);
+      assert.equal(existsSync(join(tmp, '.claude', 'skills', 'normalize')), false);
+    });
+
+    it('does NOT delete skills that do not mention impeccable', () => {
+      writeSkill(tmp, '.claude', 'arrange', 'My custom layout organizer.');
+      const deleted = removeDeprecatedSkills(tmp);
+      assert.equal(deleted.length, 0);
+      assert.equal(existsSync(join(tmp, '.claude', 'skills', 'arrange')), true);
+    });
+
+    it('deletes i-prefixed variants', () => {
+      writeSkill(tmp, '.cursor', 'i-normalize', 'Invoke /impeccable first.');
+      const deleted = removeDeprecatedSkills(tmp);
+      assert.equal(deleted.length, 1);
+      assert.equal(existsSync(join(tmp, '.cursor', 'skills', 'i-normalize')), false);
+    });
+
+    it('cleans across multiple harness directories', () => {
+      writeSkill(tmp, '.claude', 'onboard', 'Run impeccable teach first.');
+      writeSkill(tmp, '.agents', 'onboard', 'Run impeccable teach first.');
+      writeSkill(tmp, '.cursor', 'onboard', 'Run impeccable teach first.');
+      const deleted = removeDeprecatedSkills(tmp);
+      assert.equal(deleted.length, 3);
+    });
+
+    it('leaves non-deprecated skills alone', () => {
+      writeSkill(tmp, '.claude', 'polish', 'Invoke /impeccable first.');
+      writeSkill(tmp, '.claude', 'arrange', 'Invoke /impeccable first.');
+      const deleted = removeDeprecatedSkills(tmp);
+      assert.equal(deleted.length, 1); // only arrange
+      assert.equal(existsSync(join(tmp, '.claude', 'skills', 'polish')), true);
+    });
+
+    it('handles symlinks to deprecated skills', () => {
+      // Create the canonical skill in .agents
+      const canonical = writeSkill(tmp, '.agents', 'extract', 'Use impeccable extract.');
+      // Create a symlink in .claude
+      mkdirSync(join(tmp, '.claude', 'skills'), { recursive: true });
+      symlinkSync(canonical, join(tmp, '.claude', 'skills', 'extract'));
+      const deleted = removeDeprecatedSkills(tmp);
+      assert.equal(deleted.length, 2); // both canonical and symlink
+    });
+  });
+
+  describe('cleanSkillsLock', () => {
+    it('removes impeccable-owned deprecated entries', () => {
+      const lock = {
+        version: 1,
+        skills: {
+          arrange: { source: 'pbakaus/impeccable', sourceType: 'github', computedHash: 'abc' },
+          polish: { source: 'pbakaus/impeccable', sourceType: 'github', computedHash: 'def' },
+          'resolve-reviews': { source: 'pbakaus/agent-reviews', sourceType: 'github', computedHash: 'ghi' },
+        },
+      };
+      writeFileSync(join(tmp, 'skills-lock.json'), JSON.stringify(lock), 'utf-8');
+      const removed = cleanSkillsLock(tmp);
+      assert.deepEqual(removed, ['arrange']);
+      const updated = JSON.parse(readFileSync(join(tmp, 'skills-lock.json'), 'utf-8'));
+      assert.equal(updated.skills.arrange, undefined);
+      assert.ok(updated.skills.polish); // not deprecated
+      assert.ok(updated.skills['resolve-reviews']); // different source
+    });
+
+    it('does NOT remove entries from other sources', () => {
+      const lock = {
+        version: 1,
+        skills: {
+          extract: { source: 'some-other/package', sourceType: 'github', computedHash: 'xyz' },
+        },
+      };
+      writeFileSync(join(tmp, 'skills-lock.json'), JSON.stringify(lock), 'utf-8');
+      const removed = cleanSkillsLock(tmp);
+      assert.equal(removed.length, 0);
+    });
+
+    it('handles missing skills-lock.json gracefully', () => {
+      const removed = cleanSkillsLock(tmp);
+      assert.equal(removed.length, 0);
+    });
+
+    it('removes i-prefixed entries', () => {
+      const lock = {
+        version: 1,
+        skills: {
+          'i-arrange': { source: 'pbakaus/impeccable', sourceType: 'github', computedHash: 'abc' },
+          'i-normalize': { source: 'pbakaus/impeccable', sourceType: 'github', computedHash: 'def' },
+        },
+      };
+      writeFileSync(join(tmp, 'skills-lock.json'), JSON.stringify(lock), 'utf-8');
+      const removed = cleanSkillsLock(tmp);
+      assert.equal(removed.length, 2);
+    });
+  });
+
+  describe('cleanup (integration)', () => {
+    it('cleans both files and lock entries in one pass', () => {
+      // Set up deprecated skills in two harness dirs
+      writeSkill(tmp, '.claude', 'arrange', 'Invoke /impeccable.');
+      writeSkill(tmp, '.agents', 'arrange', 'Invoke /impeccable.');
+      writeSkill(tmp, '.claude', 'extract', 'Run impeccable extract.');
+
+      // Set up lock file
+      const lock = {
+        version: 1,
+        skills: {
+          arrange: { source: 'pbakaus/impeccable', sourceType: 'github', computedHash: 'a' },
+          extract: { source: 'pbakaus/impeccable', sourceType: 'github', computedHash: 'b' },
+          polish: { source: 'pbakaus/impeccable', sourceType: 'github', computedHash: 'c' },
+        },
+      };
+      writeFileSync(join(tmp, 'skills-lock.json'), JSON.stringify(lock), 'utf-8');
+
+      const result = cleanup(tmp);
+      assert.equal(result.deletedPaths.length, 3);
+      assert.equal(result.removedLockEntries.length, 2); // arrange + extract
+      assert.equal(existsSync(join(tmp, '.claude', 'skills', 'arrange')), false);
+      assert.equal(existsSync(join(tmp, '.agents', 'skills', 'arrange')), false);
+
+      const updated = JSON.parse(readFileSync(join(tmp, 'skills-lock.json'), 'utf-8'));
+      assert.ok(updated.skills.polish);
+      assert.equal(updated.skills.arrange, undefined);
+      assert.equal(updated.skills.extract, undefined);
+    });
+
+    it('is a no-op when nothing needs cleaning', () => {
+      writeSkill(tmp, '.claude', 'polish', 'Invoke /impeccable.');
+      const result = cleanup(tmp);
+      assert.equal(result.deletedPaths.length, 0);
+      assert.equal(result.removedLockEntries.length, 0);
+    });
+  });
+});

tests/detect-antipatterns-browser.test.mjs 🔗

@@ -0,0 +1,99 @@
+/**
+ * Puppeteer-backed fixture tests for browser-only detection rules.
+ *
+ * Some detection rules (cramped-padding, line-length, tight-leading,
+ * skipped-heading, justified-text, tiny-text, all-caps-body, wide-tracking,
+ * small-target) need real browser layout — they read getBoundingClientRect
+ * and getComputedStyle results that jsdom can't compute. Those rules can't
+ * be tested with the jsdom suite in detect-antipatterns-fixtures.test.mjs.
+ *
+ * This file uses detectUrl() (Puppeteer) to load fixtures in headless Chrome
+ * via a temporary static HTTP server, so the fixtures can use absolute
+ * <script src="/js/..."> paths just like in development.
+ *
+ * Run via Node's built-in test runner:
+ *   node --test tests/detect-antipatterns-browser.test.mjs
+ */
+import { describe, it, before, after } from 'node:test';
+import assert from 'node:assert/strict';
+import http from 'node:http';
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { detectUrl } from '../src/detect-antipatterns.mjs';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, '..');
+const PORT = 8765;
+const BASE = `http://localhost:${PORT}`;
+
+const MIME = {
+  '.html': 'text/html; charset=utf-8',
+  '.js': 'text/javascript; charset=utf-8',
+  '.css': 'text/css; charset=utf-8',
+  '.svg': 'image/svg+xml',
+  '.png': 'image/png',
+  '.jpg': 'image/jpeg',
+};
+
+let server;
+
+before(async () => {
+  // Static server: maps /fixtures/* to tests/fixtures/* and /js/* to public/js/*
+  // (mirrors the routes in server/index.js so fixtures can use absolute paths)
+  server = http.createServer((req, res) => {
+    let filePath;
+    if (req.url.startsWith('/fixtures/')) {
+      filePath = path.join(ROOT, 'tests', req.url);
+    } else if (req.url === '/js/detect-antipatterns-browser.js') {
+      filePath = path.join(ROOT, 'src/detect-antipatterns-browser.js');
+    } else {
+      res.writeHead(404).end();
+      return;
+    }
+    try {
+      const body = fs.readFileSync(filePath);
+      const ext = path.extname(filePath);
+      res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
+      res.end(body);
+    } catch {
+      res.writeHead(404).end();
+    }
+  });
+  await new Promise((resolve) => server.listen(PORT, resolve));
+});
+
+after(async () => {
+  await new Promise((resolve) => server.close(resolve));
+});
+
+describe('detectUrl — browser-only fixtures', () => {
+  // Only two rules genuinely need real browser layout (getBoundingClientRect):
+  //   line-length    → reads rect.width to compute chars-per-line
+  //   cramped-padding → reads rect.width/height to filter small badges
+  // Everything else in the quality.html fixture runs in jsdom and is asserted
+  // by tests/detect-antipatterns-fixtures.test.mjs.
+
+  it('cramped-padding: flag column triggers all 8 cramped cases, pass column adds none', async () => {
+    const f = await detectUrl(`${BASE}/fixtures/antipatterns/cramped-padding.html`);
+    const cramped = f.filter(r => r.antipattern === 'cramped-padding');
+    // Flag column has 8 cases that should fire under the asymmetric
+    // proportional rule (vertical: max(4, fs×0.3), horizontal: max(8, fs×0.5)):
+    //   1. 14px body / 4px all sides           — V fail
+    //   2. 14px body / 2px all sides           — both fail
+    //   3. 16px body / 4px all sides           — both fail
+    //   4. 14px body / 1px V / 16px H          — V fail
+    //   5. 14px body / 12px V / 4px H          — H fail
+    //   6. 24px heading / 8px all sides        — H fail (improvement over old 8px floor)
+    //   7. 32px hero / 6px V / 16px H          — V fail
+    //   8. 14px <pre> / 2px all sides          — both fail
+    // Pass column has 12 cases (small pills, standard cards, code blocks,
+    // buttons, inputs, big text with proportional padding) — none should fire.
+    assert.equal(cramped.length, 8, `expected 8 cramped-padding findings, got ${cramped.length}`);
+  });
+
+  it('line-length: flag column triggers, pass column adds none', async () => {
+    const f = await detectUrl(`${BASE}/fixtures/antipatterns/quality.html`);
+    assert.equal(f.filter(r => r.antipattern === 'line-length').length, 1);
+  });
+});

tests/detect-antipatterns-fixtures.test.mjs 🔗

@@ -0,0 +1,266 @@
+/**
+ * jsdom fixture tests for anti-pattern detection.
+ * Run via Node's built-in test runner (not bun) to avoid jsdom resource limits.
+ *
+ * Usage: node --test tests/detect-antipatterns-fixtures.test.mjs
+ */
+import { describe, it } from 'node:test';
+import assert from 'node:assert/strict';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import {
+  detectHtml,
+} from '../src/detect-antipatterns.mjs';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const FIXTURES = path.join(__dirname, 'fixtures', 'antipatterns');
+
+describe('detectHtml — jsdom fixtures', () => {
+  it('should-flag: catches border anti-patterns', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'should-flag.html'));
+    assert.ok(f.some(r => r.antipattern === 'side-tab'));
+    assert.ok(f.some(r => r.antipattern === 'border-accent-on-rounded'));
+  });
+
+  it('should-pass: zero border findings', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'should-pass.html'));
+    assert.equal(f.filter(r => r.antipattern === 'side-tab' || r.antipattern === 'border-accent-on-rounded').length, 0);
+  });
+
+  it('linked-stylesheet: catches borders, no false positives', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'linked-stylesheet.html'));
+    assert.ok(f.some(r => r.antipattern === 'side-tab'));
+    assert.ok(f.some(r => r.antipattern === 'border-accent-on-rounded'));
+    assert.equal(f.filter(r => r.snippet?.includes('clean')).length, 0);
+  });
+
+  it('partial-component: flags borders, skips page-level', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'partial-component.html'));
+    assert.ok(f.some(r => r.antipattern === 'side-tab'));
+    assert.equal(f.filter(r => r.antipattern === 'flat-type-hierarchy').length, 0);
+  });
+
+  it('color: flag column triggers all color rules, pass column adds none', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'color.html'));
+    // All five color rules must fire from the flag column
+    assert.ok(f.some(r => r.antipattern === 'pure-black-white'), 'expected pure-black-white');
+    assert.ok(f.some(r => r.antipattern === 'gray-on-color'), 'expected gray-on-color');
+    assert.ok(f.some(r => r.antipattern === 'low-contrast'), 'expected low-contrast');
+    assert.ok(f.some(r => r.antipattern === 'gradient-text'), 'expected gradient-text');
+    assert.ok(f.some(r => r.antipattern === 'ai-color-palette'), 'expected ai-color-palette');
+    // Gradient-bg + gray text case (added with the gradient-fix patch)
+    assert.ok(
+      f.some(r => r.antipattern === 'low-contrast' && /#808080|#3b82f6|#8b5cf6/i.test(r.snippet || '')),
+      'expected low-contrast finding for gray heading on blue/purple gradient',
+    );
+    assert.ok(
+      f.some(r => r.antipattern === 'gray-on-color' && /gradient/i.test(r.snippet || '')),
+      'expected gray-on-color finding referencing gradient',
+    );
+  });
+
+  it('color: white text on background-image url() ancestor is not flagged as low-contrast', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'color.html'));
+    // The pass column has white text on a div with background-image: url().
+    // The detector can't know the image color, so it must not assume the body
+    // bg and report a false low-contrast finding (#ffffff on #fafafa).
+    const falsePositive = f.filter(r =>
+      r.antipattern === 'low-contrast' &&
+      /#ffffff on #fafafa/i.test(r.snippet || '')
+    );
+    assert.equal(
+      falsePositive.length, 0,
+      `expected no low-contrast from bg-image ancestor, got: ${falsePositive.map(r => r.snippet).join('; ')}`
+    );
+  });
+
+  it('color: Tailwind bg-black/N opacity modifiers are not flagged as pure-black-white', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'color.html'));
+    // The pass column has bg-black/3, hover:bg-black/5, bg-black/50 — none are pure black.
+    // Only the flag column's literal bg-black class should trigger pure-black-white.
+    const pureBlackFindings = f.filter(r => r.antipattern === 'pure-black-white');
+    const opacityFalsePositives = pureBlackFindings.filter(r =>
+      (r.snippet || '').includes('bg-black') &&
+      f.some(() => true) // check that bg-black/N class triggers are absent
+    );
+    // There should be exactly the flag-column hits (bg-black class + #000000 inline)
+    // and zero from the pass-column opacity variants.
+    // The pass-column elements have data-test attributes starting with "bg-black-"
+    // The Tailwind class check produces snippet "bg-black" — count those.
+    const twSnippets = pureBlackFindings.filter(r => (r.snippet || '') === 'bg-black');
+    assert.equal(
+      twSnippets.length, 1,
+      `expected exactly 1 Tailwind bg-black finding (flag column only), got ${twSnippets.length}: ${twSnippets.map(r => r.snippet).join('; ')}`
+    );
+  });
+
+  it('color: emoji-only text is never flagged as low-contrast', async () => {
+    // Emojis render as multicolor glyphs regardless of CSS `color`, so the
+    // CSS text color is irrelevant for contrast. The fixture's emoji cards
+    // intentionally set text color to match the bg (which would trip the
+    // rule for any other text). The detector must skip emoji-only nodes.
+    const f = await detectHtml(path.join(FIXTURES, 'color.html'));
+    const emojiCardColorPairs = ['#ffe4e6 on #ffe4e6', '#1a1a1a on #1a1a1a'];
+    const matches = f.filter(r =>
+      (r.antipattern === 'low-contrast' || r.antipattern === 'gray-on-color') &&
+      emojiCardColorPairs.some(pair => (r.snippet || '').includes(pair))
+    );
+    assert.equal(
+      matches.length, 0,
+      `expected no contrast findings on emoji-only text, got: ${matches.map(r => r.snippet).join('; ')}`
+    );
+  });
+
+  it('legitimate-borders: minimal false positives', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'legitimate-borders.html'));
+    const borderFindings = f.filter(r => r.antipattern === 'side-tab' || r.antipattern === 'border-accent-on-rounded');
+    assert.ok(borderFindings.length <= 1);
+  });
+
+  it('modern-color-borders: oklch/oklab/lch/lab side-tabs are flagged, neutrals pass', async () => {
+    // Regression for the isNeutralColor bug where any non-rgb() color format
+    // (oklch, oklab, lch, lab — which jsdom does NOT normalize to rgb) was
+    // misclassified as neutral, causing checkBorders() to silently skip
+    // every element with a modern-color side border.
+    //
+    // Also regression for the SAFE_TAGS/label bug: card-shaped <label>
+    // elements (clickable checklist rows with padding + radius + colored
+    // side border) used to be silently skipped because checkBorders'
+    // SAFE_TAGS gate excluded <label>. The fix narrows that gate so card-
+    // shaped labels are checked while plain inline form labels still pass.
+    const f = await detectHtml(path.join(FIXTURES, 'modern-color-borders.html'));
+    const sideTabs = f.filter(r => r.antipattern === 'side-tab');
+    // Twelve FLAG cases: oklch x3, oklab, lch, lab — all colored border-left
+    // with a non-zero border-radius — plus two card-shaped <label> cases
+    // (one oklch, one rgb), plus four var()-based cases (shorthand, mixed
+    // neutral+colored, border-right, and a card-shaped <label>). Each must
+    // produce exactly one side-tab.
+    assert.equal(
+      sideTabs.length, 12,
+      `expected 12 side-tab findings from the FLAG column, got ${sideTabs.length}: ${sideTabs.map(r => r.snippet).join('; ')}`
+    );
+    // Eleven findings must be border-left; exactly one is border-right
+    // (the #flag-var-right case). The fixture doesn't decorate top/bottom
+    // on any flag element.
+    const leftFindings = sideTabs.filter(r => /border-left/.test(r.snippet || ''));
+    const rightFindings = sideTabs.filter(r => /border-right/.test(r.snippet || ''));
+    assert.equal(leftFindings.length, 11, `expected 11 border-left findings, got ${leftFindings.length}`);
+    assert.equal(rightFindings.length, 1, `expected 1 border-right finding, got ${rightFindings.length}`);
+    // PASS column must contribute zero border findings of either flavor.
+    // There are 13 pass cases: 6 structural neutrals plus 4 labels (plain
+    // inline form label, label with a neutral gray border, label in a form
+    // row, and a label with a thin 1px colored left border), plus 3 var()
+    // pass cases (neutral-resolving var, thin var, uniform all-sides var).
+    // If any leaks through, the label exception or var() fallback is
+    // over-broad.
+    const borderAccent = f.filter(r => r.antipattern === 'border-accent-on-rounded');
+    assert.equal(
+      borderAccent.length, 0,
+      `expected 0 border-accent-on-rounded, got ${borderAccent.length}: ${borderAccent.map(r => r.snippet).join('; ')}`
+    );
+  });
+
+  it('typography-should-flag: detects all three issues', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'typography-should-flag.html'));
+    assert.ok(f.some(r => r.antipattern === 'overused-font'));
+    assert.ok(f.some(r => r.antipattern === 'single-font'));
+    assert.ok(f.some(r => r.antipattern === 'flat-type-hierarchy'));
+  });
+
+  it('typography-should-pass: zero findings', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'typography-should-pass.html'));
+    assert.equal(f.length, 0);
+  });
+});
+
+describe('detectHtml — icon-tile-stack', () => {
+  // Two-column fixture convention: left col = should-flag, right col = should-pass.
+  // The rule's snippet embeds the heading text in quotes, e.g.
+  //   "80x80px icon tile above h3 \"Lightning Fast\"".
+  // The test extracts those quoted texts and matches them against the
+  // expected lists below.
+  const SHOULD_FLAG = [
+    'Lightning Fast',
+    'Secure Storage',
+    'Easy Setup',
+    'Powerful Analytics',
+    'Emoji Inline Icon',
+  ];
+  const SHOULD_PASS = [
+    'Sarah Chen',
+    'Article Headline',
+    'Inline Side By Side',
+    'Plain Heading No Icon',
+    'Tiny Icon Above Me',
+    'Huge Hero Image',
+  ];
+
+  it('icon-tile-stack: flags only the should-flag column', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'icon-tile-stack.html'));
+    const flagged = new Set();
+    for (const r of f) {
+      if (r.antipattern !== 'icon-tile-stack') continue;
+      const m = (r.snippet || '').match(/"([^"]+)"/);
+      if (m) flagged.add(m[1]);
+    }
+
+    for (const text of SHOULD_FLAG) {
+      assert.ok(flagged.has(text), `expected "${text}" to be flagged as icon-tile-stack`);
+    }
+    for (const text of SHOULD_PASS) {
+      assert.ok(!flagged.has(text), `"${text}" should NOT be flagged as icon-tile-stack`);
+    }
+  });
+});
+
+describe('detectHtml — quality (jsdom-compatible rules)', () => {
+  // Six of the eight quality rules can run in jsdom because they only need
+  // computed CSS values (tight-leading, tiny-text, justified-text,
+  // all-caps-body, wide-tracking) or pure DOM walks (skipped-heading).
+  // The other two (line-length, cramped-padding) need real layout rects and
+  // live in tests/detect-antipatterns-browser.test.mjs (Puppeteer-backed).
+  it('quality: flag column triggers all 6 jsdom-compatible quality rules', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'quality.html'));
+    assert.equal(f.filter(r => r.antipattern === 'tight-leading').length, 1);
+    assert.equal(f.filter(r => r.antipattern === 'tiny-text').length, 1);
+    assert.equal(f.filter(r => r.antipattern === 'justified-text').length, 1);
+    assert.equal(f.filter(r => r.antipattern === 'all-caps-body').length, 1);
+    assert.equal(f.filter(r => r.antipattern === 'wide-tracking').length, 1);
+    assert.equal(f.filter(r => r.antipattern === 'skipped-heading').length, 1);
+  });
+});
+
+describe('detectHtml — layout', () => {
+  it('layout: flag column triggers nested-cards, pass column adds none', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'layout.html'));
+    const nested = f.filter(r => r.antipattern === 'nested-cards');
+    assert.ok(nested.length >= 4, `expected ≥4 nested-cards findings, got ${nested.length}`);
+    // The page-level layout rules (monotonous-spacing, everything-centered)
+    // need Tailwind-via-CDN to render, which jsdom doesn't execute. They're
+    // effectively dormant in this test environment regardless of the fixture
+    // contents — so all we can verify is that the pass column doesn't push
+    // them awake unexpectedly.
+    assert.equal(f.filter(r => r.antipattern === 'monotonous-spacing').length, 0);
+    assert.equal(f.filter(r => r.antipattern === 'everything-centered').length, 0);
+  });
+});
+
+describe('detectHtml — motion', () => {
+  // jsdom doesn't fully apply class-based styles, so the absolute finding counts
+  // are lower than what a real browser would see. The hardcoded counts below are
+  // the calibrated jsdom baseline — if a future change pushes them up, that's a
+  // pass-column false positive; if down, the rule or fixture has regressed.
+  it('motion: flag column triggers both motion rules, pass column adds none', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'motion.html'));
+    assert.equal(f.filter(r => r.antipattern === 'bounce-easing').length, 2);
+    assert.equal(f.filter(r => r.antipattern === 'layout-transition').length, 2);
+  });
+});
+
+describe('detectHtml — dark glow', () => {
+  // Calibrated jsdom baseline — see motion test note above.
+  it('glow: flag column triggers dark-glow, pass column adds none', async () => {
+    const f = await detectHtml(path.join(FIXTURES, 'glow.html'));
+    assert.equal(f.filter(r => r.antipattern === 'dark-glow').length, 1);
+  });
+});

tests/detect-antipatterns.test.js 🔗

@@ -0,0 +1,1231 @@
+import { describe, test, expect } from 'bun:test';
+import fs from 'fs';
+import path from 'path';
+import { spawnSync } from 'child_process';
+import {
+  ANTIPATTERNS, checkElementBorders, checkElementMotion, checkElementGlow, isNeutralColor, isFullPage,
+  detectText, extractStyleBlocks, extractCSSinJS,
+  walkDir, SCANNABLE_EXTENSIONS,
+  buildImportGraph, resolveImport,
+  detectFrameworkConfig, isPortListening, FRAMEWORK_CONFIGS,
+} from '../src/detect-antipatterns.mjs';
+
+const FIXTURES = path.join(import.meta.dir, 'fixtures', 'antipatterns');
+const SCRIPT = path.join(import.meta.dir, '..', 'src', 'detect-antipatterns.mjs');
+
+
+// ---------------------------------------------------------------------------
+// Core: checkElementBorders (computed style simulation)
+// ---------------------------------------------------------------------------
+
+describe('checkElementBorders', () => {
+  function mockStyle(overrides) {
+    return { borderTopWidth: '0', borderRightWidth: '0', borderBottomWidth: '0', borderLeftWidth: '0',
+      borderTopColor: '', borderRightColor: '', borderBottomColor: '', borderLeftColor: '',
+      borderRadius: '0', ...overrides };
+  }
+
+  test('detects side-tab with radius', () => {
+    const f = checkElementBorders('div', mockStyle({
+      borderLeftWidth: '4', borderLeftColor: 'rgb(59, 130, 246)', borderRadius: '12',
+    }));
+    expect(f.length).toBe(1);
+    expect(f[0].id).toBe('side-tab');
+  });
+
+  test('detects side-tab without radius (thick)', () => {
+    const f = checkElementBorders('div', mockStyle({
+      borderLeftWidth: '4', borderLeftColor: 'rgb(59, 130, 246)',
+    }));
+    expect(f.length).toBe(1);
+    expect(f[0].id).toBe('side-tab');
+  });
+
+  test('skips side border below threshold without radius', () => {
+    const f = checkElementBorders('div', mockStyle({
+      borderLeftWidth: '2', borderLeftColor: 'rgb(59, 130, 246)',
+    }));
+    expect(f).toHaveLength(0);
+  });
+
+  test('detects border-accent-on-rounded (top)', () => {
+    const f = checkElementBorders('div', mockStyle({
+      borderTopWidth: '3', borderTopColor: 'rgb(139, 92, 246)', borderRadius: '12',
+    }));
+    expect(f.length).toBe(1);
+    expect(f[0].id).toBe('border-accent-on-rounded');
+  });
+
+  test('skips safe tags', () => {
+    const f = checkElementBorders('blockquote', mockStyle({
+      borderLeftWidth: '4', borderLeftColor: 'rgb(59, 130, 246)',
+    }));
+    expect(f).toHaveLength(0);
+  });
+
+  test('skips neutral colors', () => {
+    const f = checkElementBorders('div', mockStyle({
+      borderLeftWidth: '4', borderLeftColor: 'rgb(200, 200, 200)',
+    }));
+    expect(f).toHaveLength(0);
+  });
+
+  test('skips uniform borders (not accent)', () => {
+    const f = checkElementBorders('div', mockStyle({
+      borderTopWidth: '2', borderRightWidth: '2', borderBottomWidth: '2', borderLeftWidth: '2',
+      borderTopColor: 'rgb(59, 130, 246)', borderRightColor: 'rgb(59, 130, 246)',
+      borderBottomColor: 'rgb(59, 130, 246)', borderLeftColor: 'rgb(59, 130, 246)',
+    }));
+    expect(f).toHaveLength(0);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// isNeutralColor
+// ---------------------------------------------------------------------------
+
+describe('isNeutralColor', () => {
+  test('gray is neutral', () => expect(isNeutralColor('rgb(200, 200, 200)')).toBe(true));
+  test('blue is not neutral', () => expect(isNeutralColor('rgb(59, 130, 246)')).toBe(false));
+  test('transparent is neutral', () => expect(isNeutralColor('transparent')).toBe(true));
+  test('null is neutral', () => expect(isNeutralColor(null)).toBe(true));
+});
+
+// ---------------------------------------------------------------------------
+// Regex fallback (detectText)
+// ---------------------------------------------------------------------------
+
+describe('detectText — Tailwind side-tab', () => {
+  test('detects border-l-4 (thick, no rounded needed)', () => {
+    const f = detectText('<div class="border-l-4 border-blue-500">', 'test.html');
+    expect(f.some(r => r.antipattern === 'side-tab')).toBe(true);
+  });
+
+  test('detects border-l-1 + rounded', () => {
+    const f = detectText('<div class="border-l-1 border-blue-500 rounded-md">', 'test.html');
+    expect(f.some(r => r.antipattern === 'side-tab')).toBe(true);
+  });
+
+  test('ignores border-l-1 without rounded', () => {
+    const f = detectText('<div class="border-l-1 border-gray-300">', 'test.html');
+    expect(f.filter(r => r.antipattern === 'side-tab')).toHaveLength(0);
+  });
+
+  test('ignores border-t without rounded', () => {
+    const f = detectText('<div class="border-t-4 border-b-4">', 'test.html');
+    expect(f.filter(r => r.antipattern === 'border-accent-on-rounded')).toHaveLength(0);
+  });
+});
+
+describe('detectText — CSS borders', () => {
+  test('detects border-left shorthand', () => {
+    const f = detectText('.card { border-left: 4px solid #3b82f6; }', 'test.css');
+    expect(f.some(r => r.antipattern === 'side-tab')).toBe(true);
+  });
+
+  test('ignores neutral border', () => {
+    const f = detectText('.card { border-left: 4px solid #e5e7eb; }', 'test.css');
+    expect(f.filter(r => r.antipattern === 'side-tab')).toHaveLength(0);
+  });
+
+  test('skips blockquote', () => {
+    const f = detectText('<blockquote style="border-left: 4px solid #ccc;">', 'test.html');
+    expect(f.filter(r => r.antipattern === 'side-tab')).toHaveLength(0);
+  });
+});
+
+describe('detectText — overused fonts', () => {
+  test('detects Inter', () => {
+    const f = detectText("body { font-family: 'Inter', sans-serif; }", 'test.css');
+    expect(f.some(r => r.antipattern === 'overused-font')).toBe(true);
+  });
+
+  test('does not flag distinctive fonts', () => {
+    const f = detectText("body { font-family: 'Instrument Sans', sans-serif; }", 'test.css');
+    expect(f.filter(r => r.antipattern === 'overused-font')).toHaveLength(0);
+  });
+});
+
+describe('detectText — flat type hierarchy', () => {
+  test('flags sizes too close together', () => {
+    const page = '<!DOCTYPE html><html><style>h1{font-size:18px}h2{font-size:16px}h3{font-size:15px}p{font-size:14px}.s{font-size:13px}</style></html>';
+    const f = detectText(page, 'test.html');
+    expect(f.some(r => r.antipattern === 'flat-type-hierarchy')).toBe(true);
+  });
+
+  test('passes good hierarchy', () => {
+    const page = '<!DOCTYPE html><html><style>h1{font-size:48px}h2{font-size:32px}p{font-size:16px}.s{font-size:12px}</style></html>';
+    const f = detectText(page, 'test.html');
+    expect(f.filter(r => r.antipattern === 'flat-type-hierarchy')).toHaveLength(0);
+  });
+});
+
+// jsdom fixture tests moved to detect-antipatterns-fixtures.test.mjs (run via node --test)
+
+// ---------------------------------------------------------------------------
+// Full page vs partial detection
+// ---------------------------------------------------------------------------
+
+describe('isFullPage', () => {
+  test('detects DOCTYPE', () => expect(isFullPage('<!DOCTYPE html><html>')).toBe(true));
+  test('detects <html>', () => expect(isFullPage('<html><head></head>')).toBe(true));
+  test('detects <head>', () => expect(isFullPage('<head><meta charset="UTF-8"></head>')).toBe(true));
+  test('rejects component/partial', () => expect(isFullPage('<div class="card">content</div>')).toBe(false));
+  test('rejects JSX', () => expect(isFullPage('export default function Card() { return <div>hi</div> }')).toBe(false));
+});
+
+describe('partials skip page-level checks', () => {
+  test('regex: partial with flat hierarchy is not flagged', () => {
+    const partial = '<div style="font-size: 14px">text</div>\n<div style="font-size: 16px">text</div>\n<div style="font-size: 15px">text</div>';
+    const f = detectText(partial, 'card.tsx');
+    expect(f.filter(r => r.antipattern === 'flat-type-hierarchy')).toHaveLength(0);
+  });
+
+  test('regex: partial with single overused font is not flagged for single-font', () => {
+    const partial = `<div style="font-family: 'Inter', sans-serif; font-size: 14px">text</div>\n`.repeat(25);
+    const f = detectText(partial, 'card.tsx');
+    expect(f.filter(r => r.antipattern === 'single-font')).toHaveLength(0);
+  });
+
+  test('regex: partial still flags border anti-patterns', () => {
+    const partial = '<div class="border-l-4 border-blue-500 rounded-lg">card</div>';
+    const f = detectText(partial, 'card.tsx');
+    expect(f.some(r => r.antipattern === 'side-tab')).toBe(true);
+  });
+
+  test('regex: full page with flat hierarchy IS flagged', () => {
+    const page = '<!DOCTYPE html><html><head></head><body>\n' +
+      '<h1 style="font-size: 18px">h1</h1>\n<h2 style="font-size: 16px">h2</h2>\n' +
+      '<p style="font-size: 14px">p</p>\n<span style="font-size: 15px">s</span>\n' +
+      '<small style="font-size: 13px">sm</small>\n</body></html>';
+    const f = detectText(page, 'index.html');
+    expect(f.some(r => r.antipattern === 'flat-type-hierarchy')).toBe(true);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Layout anti-patterns
+// ---------------------------------------------------------------------------
+
+describe('detectHtml — layout', () => {
+  test('detects monotonous spacing via regex', () => {
+    // A page where every padding/margin is 16px
+    const html = '<!DOCTYPE html><html><body>' +
+      '<div style="padding: 16px; margin-bottom: 16px;"><p style="margin-bottom: 16px;">a</p></div>'.repeat(5) +
+      '</body></html>';
+    const f = detectText(html, 'test.html');
+    expect(f.some(r => r.antipattern === 'monotonous-spacing')).toBe(true);
+  });
+
+  test('detects everything centered via regex', () => {
+    const html = `<!DOCTYPE html><html><body>
+<h1 style="text-align: center;">Title</h1>
+<p style="text-align: center;">Paragraph one more text here</p>
+<p style="text-align: center;">Paragraph two more text here</p>
+<p style="text-align: center;">Paragraph three more text here</p>
+<p style="text-align: center;">Paragraph four more text here</p>
+<p style="text-align: center;">Paragraph five more text here</p>
+<p style="text-align: center;">Paragraph six more text here</p>
+</body></html>`;
+    const f = detectText(html, 'test.html');
+    expect(f.some(r => r.antipattern === 'everything-centered')).toBe(true);
+  });
+
+});
+
+// ---------------------------------------------------------------------------
+// Motion anti-patterns
+// ---------------------------------------------------------------------------
+
+describe('checkElementMotion', () => {
+  function mockStyle(overrides) {
+    return { transitionProperty: '', animationName: 'none', animationTimingFunction: '', transitionTimingFunction: '', ...overrides };
+  }
+
+  test('detects bounce animation name', () => {
+    const f = checkElementMotion('div', mockStyle({ animationName: 'bounce' }));
+    expect(f.some(r => r.id === 'bounce-easing')).toBe(true);
+  });
+
+  test('detects elastic animation name', () => {
+    const f = checkElementMotion('div', mockStyle({ animationName: 'elastic-in' }));
+    expect(f.some(r => r.id === 'bounce-easing')).toBe(true);
+  });
+
+  test('detects overshoot cubic-bezier in animation timing', () => {
+    const f = checkElementMotion('div', mockStyle({
+      animationTimingFunction: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
+    }));
+    expect(f.some(r => r.id === 'bounce-easing')).toBe(true);
+  });
+
+  test('detects overshoot cubic-bezier in transition timing', () => {
+    const f = checkElementMotion('div', mockStyle({
+      transitionTimingFunction: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
+    }));
+    expect(f.some(r => r.id === 'bounce-easing')).toBe(true);
+  });
+
+  test('passes standard ease-out-quart', () => {
+    const f = checkElementMotion('div', mockStyle({
+      transitionTimingFunction: 'cubic-bezier(0.25, 1, 0.5, 1)',
+    }));
+    expect(f.filter(r => r.id === 'bounce-easing')).toHaveLength(0);
+  });
+
+  test('passes standard ease', () => {
+    const f = checkElementMotion('div', mockStyle({
+      transitionTimingFunction: 'cubic-bezier(0.25, 0.1, 0.25, 1.0)',
+    }));
+    expect(f.filter(r => r.id === 'bounce-easing')).toHaveLength(0);
+  });
+
+  test('detects width transition', () => {
+    const f = checkElementMotion('div', mockStyle({ transitionProperty: 'width' }));
+    expect(f.some(r => r.id === 'layout-transition')).toBe(true);
+  });
+
+  test('detects height transition', () => {
+    const f = checkElementMotion('div', mockStyle({ transitionProperty: 'height' }));
+    expect(f.some(r => r.id === 'layout-transition')).toBe(true);
+  });
+
+  test('detects padding transition', () => {
+    const f = checkElementMotion('div', mockStyle({ transitionProperty: 'padding' }));
+    expect(f.some(r => r.id === 'layout-transition')).toBe(true);
+  });
+
+  test('detects margin transition', () => {
+    const f = checkElementMotion('div', mockStyle({ transitionProperty: 'margin' }));
+    expect(f.some(r => r.id === 'layout-transition')).toBe(true);
+  });
+
+  test('detects max-height transition', () => {
+    const f = checkElementMotion('div', mockStyle({ transitionProperty: 'max-height' }));
+    expect(f.some(r => r.id === 'layout-transition')).toBe(true);
+  });
+
+  test('detects layout prop among mixed transitions', () => {
+    const f = checkElementMotion('div', mockStyle({ transitionProperty: 'opacity, width, color' }));
+    expect(f.some(r => r.id === 'layout-transition')).toBe(true);
+  });
+
+  test('passes transform transition', () => {
+    const f = checkElementMotion('div', mockStyle({ transitionProperty: 'transform' }));
+    expect(f.filter(r => r.id === 'layout-transition')).toHaveLength(0);
+  });
+
+  test('passes opacity transition', () => {
+    const f = checkElementMotion('div', mockStyle({ transitionProperty: 'opacity' }));
+    expect(f.filter(r => r.id === 'layout-transition')).toHaveLength(0);
+  });
+
+  test('skips transition: all', () => {
+    const f = checkElementMotion('div', mockStyle({ transitionProperty: 'all' }));
+    expect(f.filter(r => r.id === 'layout-transition')).toHaveLength(0);
+  });
+
+  test('skips safe tags', () => {
+    const f = checkElementMotion('button', mockStyle({
+      animationName: 'bounce', transitionProperty: 'width',
+    }));
+    expect(f).toHaveLength(0);
+  });
+});
+
+describe('detectText — motion', () => {
+  test('detects animate-bounce Tailwind class', () => {
+    const f = detectText('<div class="animate-bounce">loading</div>', 'test.html');
+    expect(f.some(r => r.antipattern === 'bounce-easing')).toBe(true);
+  });
+
+  test('detects animation: bounce CSS', () => {
+    const f = detectText('.icon { animation: bounce 1s infinite; }', 'test.css');
+    expect(f.some(r => r.antipattern === 'bounce-easing')).toBe(true);
+  });
+
+  test('detects animation-name: elastic', () => {
+    const f = detectText('.card { animation-name: elastic; }', 'test.css');
+    expect(f.some(r => r.antipattern === 'bounce-easing')).toBe(true);
+  });
+
+  test('detects overshoot cubic-bezier', () => {
+    const f = detectText('.btn { transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); }', 'test.css');
+    expect(f.some(r => r.antipattern === 'bounce-easing')).toBe(true);
+  });
+
+  test('passes standard cubic-bezier', () => {
+    const f = detectText('.btn { transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1); }', 'test.css');
+    expect(f.filter(r => r.antipattern === 'bounce-easing')).toHaveLength(0);
+  });
+
+  test('detects transition: width', () => {
+    const f = detectText('.sidebar { transition: width 0.3s ease; }', 'test.css');
+    expect(f.some(r => r.antipattern === 'layout-transition')).toBe(true);
+  });
+
+  test('detects transition: height', () => {
+    const f = detectText('.panel { transition: height 0.4s ease-out; }', 'test.css');
+    expect(f.some(r => r.antipattern === 'layout-transition')).toBe(true);
+  });
+
+  test('detects transition: max-height', () => {
+    const f = detectText('.accordion { transition: max-height 0.5s ease; }', 'test.css');
+    expect(f.some(r => r.antipattern === 'layout-transition')).toBe(true);
+  });
+
+  test('detects transition-property: width', () => {
+    const f = detectText('.box { transition-property: width; transition-duration: 0.3s; }', 'test.css');
+    expect(f.some(r => r.antipattern === 'layout-transition')).toBe(true);
+  });
+
+  test('skips transition: all', () => {
+    const f = detectText('.card { transition: all 0.3s ease; }', 'test.css');
+    expect(f.filter(r => r.antipattern === 'layout-transition')).toHaveLength(0);
+  });
+
+  test('skips transition: transform', () => {
+    const f = detectText('.card { transition: transform 0.3s ease; }', 'test.css');
+    expect(f.filter(r => r.antipattern === 'layout-transition')).toHaveLength(0);
+  });
+
+  test('skips transition: opacity', () => {
+    const f = detectText('.btn { transition: opacity 0.2s ease; }', 'test.css');
+    expect(f.filter(r => r.antipattern === 'layout-transition')).toHaveLength(0);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Dark glow anti-pattern
+// ---------------------------------------------------------------------------
+
+describe('checkElementGlow', () => {
+  function mockStyle(overrides) {
+    return { boxShadow: 'none', backgroundColor: '', ...overrides };
+  }
+
+  // Dark bg = luminance < 0.1 (e.g. #111827 = gray-900)
+  const darkBg = { r: 17, g: 24, b: 39 }; // #111827
+  const lightBg = { r: 249, g: 250, b: 251 }; // #f9fafb
+  const mediumBg = { r: 107, g: 114, b: 128 }; // #6b7280
+
+  test('detects blue glow on dark background', () => {
+    const f = checkElementGlow('div', mockStyle({
+      boxShadow: 'rgba(59, 130, 246, 0.4) 0px 0px 20px 0px',
+    }), darkBg);
+    expect(f.some(r => r.id === 'dark-glow')).toBe(true);
+  });
+
+  test('detects purple glow on dark background', () => {
+    const f = checkElementGlow('div', mockStyle({
+      boxShadow: 'rgba(139, 92, 246, 0.35) 0px 0px 25px 0px',
+    }), darkBg);
+    expect(f.some(r => r.id === 'dark-glow')).toBe(true);
+  });
+
+  test('detects glow in multi-shadow', () => {
+    const f = checkElementGlow('div', mockStyle({
+      boxShadow: 'rgba(0, 0, 0, 0.3) 0px 4px 6px 0px, rgba(168, 85, 247, 0.3) 0px 0px 30px 0px',
+    }), darkBg);
+    expect(f.some(r => r.id === 'dark-glow')).toBe(true);
+  });
+
+  test('passes gray shadow on dark background', () => {
+    const f = checkElementGlow('div', mockStyle({
+      boxShadow: 'rgba(0, 0, 0, 0.4) 0px 4px 12px 0px',
+    }), darkBg);
+    expect(f.filter(r => r.id === 'dark-glow')).toHaveLength(0);
+  });
+
+  test('passes colored shadow on light background', () => {
+    const f = checkElementGlow('div', mockStyle({
+      boxShadow: 'rgba(59, 130, 246, 0.4) 0px 0px 20px 0px',
+    }), lightBg);
+    expect(f.filter(r => r.id === 'dark-glow')).toHaveLength(0);
+  });
+
+  test('passes colored shadow on medium gray background', () => {
+    const f = checkElementGlow('div', mockStyle({
+      boxShadow: 'rgba(59, 130, 246, 0.5) 0px 0px 20px 0px',
+    }), mediumBg);
+    expect(f.filter(r => r.id === 'dark-glow')).toHaveLength(0);
+  });
+
+  test('passes focus ring (spread only, no blur)', () => {
+    const f = checkElementGlow('div', mockStyle({
+      boxShadow: 'rgba(59, 130, 246, 0.5) 0px 0px 0px 3px',
+    }), darkBg);
+    expect(f.filter(r => r.id === 'dark-glow')).toHaveLength(0);
+  });
+
+  test('passes subtle shadow (blur < 5px)', () => {
+    const f = checkElementGlow('div', mockStyle({
+      boxShadow: 'rgba(59, 130, 246, 0.2) 0px 1px 3px 0px',
+    }), darkBg);
+    expect(f.filter(r => r.id === 'dark-glow')).toHaveLength(0);
+  });
+
+  test('passes no shadow', () => {
+    const f = checkElementGlow('div', mockStyle({ boxShadow: 'none' }), darkBg);
+    expect(f.filter(r => r.id === 'dark-glow')).toHaveLength(0);
+  });
+
+  test('detects glow on buttons (not skipped by safe tags)', () => {
+    const f = checkElementGlow('button', mockStyle({
+      boxShadow: 'rgba(59, 130, 246, 0.4) 0px 0px 20px 0px',
+    }), darkBg);
+    expect(f.some(r => r.id === 'dark-glow')).toBe(true);
+  });
+});
+
+describe('detectText — dark glow', () => {
+  test('detects colored box-shadow glow on dark background', () => {
+    const html = '<!DOCTYPE html><html><body style="background: #111827;"><div style="box-shadow: 0 0 20px rgba(59, 130, 246, 0.4);">glow</div></body></html>';
+    const f = detectText(html, 'test.html');
+    expect(f.some(r => r.antipattern === 'dark-glow')).toBe(true);
+  });
+
+  test('skips gray shadow on dark background', () => {
+    const html = '<!DOCTYPE html><html><body style="background: #111827;"><div style="box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);">shadow</div></body></html>';
+    const f = detectText(html, 'test.html');
+    expect(f.filter(r => r.antipattern === 'dark-glow')).toHaveLength(0);
+  });
+
+  test('skips colored shadow on light page', () => {
+    const html = '<!DOCTYPE html><html><body style="background: #f9fafb;"><div style="box-shadow: 0 0 20px rgba(59, 130, 246, 0.4);">glow</div></body></html>';
+    const f = detectText(html, 'test.html');
+    expect(f.filter(r => r.antipattern === 'dark-glow')).toHaveLength(0);
+  });
+});
+
+
+// ---------------------------------------------------------------------------
+// ANTIPATTERNS registry
+// ---------------------------------------------------------------------------
+
+describe('ANTIPATTERNS registry', () => {
+  test('has at least 5 entries', () => {
+    expect(ANTIPATTERNS.length).toBeGreaterThanOrEqual(5);
+  });
+
+  test('each entry has required fields', () => {
+    for (const ap of ANTIPATTERNS) {
+      expect(ap.id).toBeTypeOf('string');
+      expect(ap.name).toBeTypeOf('string');
+      expect(ap.description).toBeTypeOf('string');
+    }
+  });
+});
+
+// ---------------------------------------------------------------------------
+// walkDir
+// ---------------------------------------------------------------------------
+
+describe('walkDir', () => {
+  test('finds scannable files', () => {
+    const files = walkDir(FIXTURES);
+    expect(files.length).toBeGreaterThanOrEqual(3);
+    expect(files.every(f => SCANNABLE_EXTENSIONS.has(path.extname(f)))).toBe(true);
+  });
+
+  test('returns empty for nonexistent dir', () => {
+    expect(walkDir('/nonexistent/path/12345')).toHaveLength(0);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// CLI integration
+// ---------------------------------------------------------------------------
+
+describe('CLI', () => {
+  function run(...args) {
+    const result = spawnSync('node', [SCRIPT, ...args], { encoding: 'utf-8', timeout: 15000 });
+    return { stdout: result.stdout || '', stderr: result.stderr || '', code: result.status };
+  }
+
+  test('--help exits 0', () => {
+    const { stdout, code } = run('--help');
+    expect(code).toBe(0);
+    expect(stdout).toContain('Usage:');
+  });
+
+  test('should-pass exits 0', () => {
+    const { code } = run(path.join(FIXTURES, 'should-pass.html'));
+    expect(code).toBe(0);
+  });
+
+  test('should-flag exits 2 with findings', () => {
+    const { code, stderr } = run(path.join(FIXTURES, 'should-flag.html'));
+    expect(code).toBe(2);
+    expect(stderr).toContain('side-tab');
+  });
+
+  test('--json outputs valid JSON', () => {
+    const { stderr, code } = run('--json', path.join(FIXTURES, 'should-flag.html'));
+    expect(code).toBe(2);
+    const parsed = JSON.parse(stderr.trim());
+    expect(parsed).toBeArray();
+    expect(parsed.length).toBeGreaterThan(0);
+  });
+
+  test('--json on clean file outputs empty array', () => {
+    const { stdout, code } = run('--json', path.join(FIXTURES, 'should-pass.html'));
+    expect(code).toBe(0);
+    expect(JSON.parse(stdout.trim())).toEqual([]);
+  });
+
+  test('--fast mode works', () => {
+    const { code } = run('--fast', path.join(FIXTURES, 'should-flag.html'));
+    expect(code).toBe(2);
+  });
+
+  test('linked stylesheet detected (jsdom default)', () => {
+    const { code, stderr } = run(path.join(FIXTURES, 'linked-stylesheet.html'));
+    expect(code).toBe(2);
+    expect(stderr).toContain('side-tab');
+  });
+
+  test('warns on nonexistent path', () => {
+    const { stderr } = run('/nonexistent/file/xyz.html');
+    expect(stderr).toContain('Warning');
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Tier 1: Vue/Svelte <style> block extraction
+// ---------------------------------------------------------------------------
+
+describe('extractStyleBlocks', () => {
+  test('extracts single <style> block from Vue SFC', () => {
+    const vue = `<template><div>hi</div></template>
+<style scoped>
+.card { border-left: 4px solid blue; }
+</style>`;
+    const blocks = extractStyleBlocks(vue, '.vue');
+    expect(blocks.length).toBe(1);
+    expect(blocks[0].content).toContain('border-left: 4px solid blue');
+    expect(blocks[0].startLine).toBeGreaterThan(1);
+  });
+
+  test('extracts multiple <style> blocks', () => {
+    const vue = `<template><div>hi</div></template>
+<style>
+.a { color: red; }
+</style>
+<style scoped>
+.b { color: blue; }
+</style>`;
+    const blocks = extractStyleBlocks(vue, '.vue');
+    expect(blocks.length).toBe(2);
+  });
+
+  test('extracts <style> from Svelte', () => {
+    const svelte = `<div>hi</div>
+<style>
+.sidebar { border-right: 4px solid #8b5cf6; }
+</style>`;
+    const blocks = extractStyleBlocks(svelte, '.svelte');
+    expect(blocks.length).toBe(1);
+    expect(blocks[0].content).toContain('border-right: 4px solid');
+  });
+
+  test('returns empty for non-Vue/Svelte files', () => {
+    const jsx = 'export function Card() { return <div>hi</div>; }';
+    expect(extractStyleBlocks(jsx, '.jsx')).toHaveLength(0);
+    expect(extractStyleBlocks(jsx, '.tsx')).toHaveLength(0);
+  });
+
+  test('returns empty when no <style> blocks exist', () => {
+    const vue = '<template><div>hi</div></template><script>export default {}</script>';
+    expect(extractStyleBlocks(vue, '.vue')).toHaveLength(0);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Tier 1: CSS-in-JS extraction
+// ---------------------------------------------------------------------------
+
+describe('extractCSSinJS', () => {
+  test('extracts styled-components template literal', () => {
+    const tsx = "const Card = styled.div`\n  border-left: 4px solid blue;\n  padding: 16px;\n`;";
+    const blocks = extractCSSinJS(tsx, '.tsx');
+    expect(blocks.length).toBeGreaterThanOrEqual(1);
+    expect(blocks.some(b => b.content.includes('border-left: 4px solid'))).toBe(true);
+  });
+
+  test('extracts styled(Component) template literal', () => {
+    const tsx = "const Box = styled(BaseBox)`\n  border-right: 5px solid #8b5cf6;\n`;";
+    const blocks = extractCSSinJS(tsx, '.tsx');
+    expect(blocks.length).toBeGreaterThanOrEqual(1);
+    expect(blocks.some(b => b.content.includes('border-right: 5px solid'))).toBe(true);
+  });
+
+  test('extracts emotion css template literal', () => {
+    const tsx = "const style = css`\n  animation: bounce 1s infinite;\n`;";
+    const blocks = extractCSSinJS(tsx, '.tsx');
+    expect(blocks.length).toBeGreaterThanOrEqual(1);
+    expect(blocks.some(b => b.content.includes('animation: bounce'))).toBe(true);
+  });
+
+  test('returns empty for non-JS files', () => {
+    expect(extractCSSinJS('.card { color: red; }', '.css')).toHaveLength(0);
+    expect(extractCSSinJS('<div>hi</div>', '.html')).toHaveLength(0);
+  });
+
+  test('returns empty when no CSS-in-JS patterns exist', () => {
+    const tsx = "function Card() { return <div className='p-4'>hi</div>; }";
+    expect(extractCSSinJS(tsx, '.tsx')).toHaveLength(0);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Tier 1: detectText on Vue/Svelte files (style blocks + template classes)
+// ---------------------------------------------------------------------------
+
+describe('detectText -- Vue SFC', () => {
+  test('detects side-tab in <style> block', () => {
+    const vue = `<template><div class="card">hi</div></template>
+<style scoped>
+.card { border-left: 4px solid #3b82f6; border-radius: 12px; }
+</style>`;
+    const f = detectText(vue, 'Card.vue');
+    expect(f.some(r => r.antipattern === 'side-tab')).toBe(true);
+  });
+
+  test('detects overused font in <style> block', () => {
+    const vue = `<template><div>hi</div></template>
+<style>
+body { font-family: 'Inter', sans-serif; }
+</style>`;
+    const f = detectText(vue, 'App.vue');
+    expect(f.some(r => r.antipattern === 'overused-font')).toBe(true);
+  });
+
+  test('detects bounce animation in <style> block', () => {
+    const vue = `<template><div>hi</div></template>
+<style>
+.item { animation: bounce 1s infinite; }
+</style>`;
+    const f = detectText(vue, 'Card.vue');
+    expect(f.some(r => r.antipattern === 'bounce-easing')).toBe(true);
+  });
+
+  test('detects gradient-text in <style> block', () => {
+    const vue = `<template><div>hi</div></template>
+<style>
+h1 { background: linear-gradient(to right, purple, cyan); -webkit-background-clip: text; background-clip: text; }
+</style>`;
+    const f = detectText(vue, 'Hero.vue');
+    expect(f.some(r => r.antipattern === 'gradient-text')).toBe(true);
+  });
+
+  test('detects Tailwind anti-patterns in <template>', () => {
+    const vue = `<template>
+  <div class="border-l-4 border-blue-500 rounded-lg">card</div>
+</template>`;
+    const f = detectText(vue, 'Card.vue');
+    expect(f.some(r => r.antipattern === 'side-tab')).toBe(true);
+  });
+});
+
+describe('detectText -- Svelte', () => {
+  test('detects side-tab in <style> block', () => {
+    const svelte = `<div>hi</div>
+<style>
+.sidebar { border-right: 4px solid #8b5cf6; border-radius: 16px; }
+</style>`;
+    const f = detectText(svelte, 'Sidebar.svelte');
+    expect(f.some(r => r.antipattern === 'side-tab')).toBe(true);
+  });
+
+  test('detects overused font in <style> block', () => {
+    const svelte = `<div>hi</div>
+<style>
+.app { font-family: 'Roboto', sans-serif; }
+</style>`;
+    const f = detectText(svelte, 'App.svelte');
+    expect(f.some(r => r.antipattern === 'overused-font')).toBe(true);
+  });
+
+  test('detects layout transition in <style> block', () => {
+    const svelte = `<div>hi</div>
+<style>
+.panel { transition: height 0.4s ease; }
+</style>`;
+    const f = detectText(svelte, 'Panel.svelte');
+    expect(f.some(r => r.antipattern === 'layout-transition')).toBe(true);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Tier 1: detectText on CSS-in-JS files
+// ---------------------------------------------------------------------------
+
+describe('detectText -- CSS-in-JS', () => {
+  test('detects side-tab in styled-components', () => {
+    const tsx = "const Card = styled.div`\n  border-left: 4px solid #3b82f6;\n  border-radius: 12px;\n`;";
+    const f = detectText(tsx, 'Card.tsx');
+    expect(f.some(r => r.antipattern === 'side-tab')).toBe(true);
+  });
+
+  test('detects bounce in emotion css', () => {
+    const tsx = "const style = css`\n  animation: bounce 1s infinite;\n`;";
+    const f = detectText(tsx, 'anim.ts');
+    expect(f.some(r => r.antipattern === 'bounce-easing')).toBe(true);
+  });
+
+  test('detects overused font in styled-components', () => {
+    const tsx = "const Wrapper = styled.main`\n  font-family: 'Inter', sans-serif;\n`;";
+    const f = detectText(tsx, 'Layout.tsx');
+    expect(f.some(r => r.antipattern === 'overused-font')).toBe(true);
+  });
+
+  test('detects gradient-text in styled-components', () => {
+    const tsx = "const Title = styled.h1`\n  background: linear-gradient(to right, purple, cyan);\n  -webkit-background-clip: text;\n  background-clip: text;\n`;";
+    const f = detectText(tsx, 'Hero.tsx');
+    expect(f.some(r => r.antipattern === 'gradient-text')).toBe(true);
+  });
+
+  test('detects pure-black-white in styled-components', () => {
+    const tsx = "const Dark = styled.section`\n  background-color: #000000;\n`;";
+    const f = detectText(tsx, 'Dark.tsx');
+    expect(f.some(r => r.antipattern === 'pure-black-white')).toBe(true);
+  });
+
+  test('does not false-positive on clean CSS-in-JS', () => {
+    const tsx = "const Card = styled.div`\n  border-radius: 12px;\n  padding: 24px;\n`;";
+    const f = detectText(tsx, 'Card.tsx');
+    expect(f.filter(r => r.antipattern === 'side-tab')).toHaveLength(0);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Tier 1: Fixture file integration tests (CLI)
+// ---------------------------------------------------------------------------
+
+describe('CLI -- framework fixtures', () => {
+  function run(...args) {
+    const result = spawnSync('node', [SCRIPT, ...args], { encoding: 'utf-8', timeout: 15000 });
+    return { stdout: result.stdout || '', stderr: result.stderr || '', code: result.status };
+  }
+
+  test('jsx-should-flag catches anti-patterns', () => {
+    const { code, stderr } = run(path.join(FIXTURES, 'jsx-should-flag.jsx'));
+    expect(code).toBe(2);
+    expect(stderr).toContain('side-tab');
+  });
+
+  test('jsx-should-pass is clean', () => {
+    const { code } = run(path.join(FIXTURES, 'jsx-should-pass.jsx'));
+    expect(code).toBe(0);
+  });
+
+  test('vue-should-flag catches anti-patterns', () => {
+    const { code, stderr } = run(path.join(FIXTURES, 'vue-should-flag.vue'));
+    expect(code).toBe(2);
+    expect(stderr).toContain('side-tab');
+  });
+
+  test('vue-should-pass is clean', () => {
+    const { code } = run(path.join(FIXTURES, 'vue-should-pass.vue'));
+    expect(code).toBe(0);
+  });
+
+  test('svelte-should-flag catches anti-patterns', () => {
+    const { code, stderr } = run(path.join(FIXTURES, 'svelte-should-flag.svelte'));
+    expect(code).toBe(2);
+    expect(stderr).toContain('side-tab');
+  });
+
+  test('svelte-should-pass is clean', () => {
+    const { code } = run(path.join(FIXTURES, 'svelte-should-pass.svelte'));
+    expect(code).toBe(0);
+  });
+
+  test('cssinjs-should-flag catches anti-patterns', () => {
+    const { code, stderr } = run(path.join(FIXTURES, 'cssinjs-should-flag.tsx'));
+    expect(code).toBe(2);
+    expect(stderr).toContain('side-tab');
+  });
+
+  test('cssinjs-should-pass is clean', () => {
+    const { code } = run(path.join(FIXTURES, 'cssinjs-should-pass.tsx'));
+    expect(code).toBe(0);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Realistic Next.js project fixtures
+// ---------------------------------------------------------------------------
+
+describe('CLI -- Next.js + Tailwind project', () => {
+  const dir = path.join(FIXTURES, 'framework-next-tailwind');
+  let stderr;
+
+  function run(...args) {
+    const result = spawnSync('node', [SCRIPT, ...args], { encoding: 'utf-8', timeout: 15000 });
+    return { stdout: result.stdout || '', stderr: result.stderr || '', code: result.status };
+  }
+
+  test('finds all expected anti-pattern types', () => {
+    const result = run(dir);
+    stderr = result.stderr;
+    expect(result.code).toBe(2);
+    for (const ap of ['side-tab', 'gradient-text', 'ai-color-palette', 'overused-font', 'bounce-easing', 'pure-black-white']) {
+      expect(stderr).toContain(ap);
+    }
+  });
+
+  test('FeatureCard: side-tab + ai-color-palette + bounce-easing', () => {
+    const { stderr } = run(path.join(dir, 'components', 'FeatureCard.tsx'));
+    expect(stderr).toContain('side-tab');
+    expect(stderr).toContain('border-l-4');
+    expect(stderr).toContain('ai-color-palette');
+    expect(stderr).toContain('text-purple-600');
+    expect(stderr).toContain('bounce-easing');
+    expect(stderr).toContain('animate-bounce');
+  });
+
+  test('PricingCard: pure-black-white + gradient-text + ai-color-palette', () => {
+    const { stderr } = run(path.join(dir, 'components', 'PricingCard.tsx'));
+    expect(stderr).toContain('pure-black-white');
+    expect(stderr).toContain('bg-black');
+    expect(stderr).toContain('gradient-text');
+    expect(stderr).toContain('bg-clip-text');
+    expect(stderr).toContain('ai-color-palette');
+  });
+
+  test('globals.css: overused Inter font', () => {
+    const { stderr } = run(path.join(dir, 'app', 'globals.css'));
+    expect(stderr).toContain('overused-font');
+    expect(stderr).toContain('Inter');
+  });
+
+  test('page.tsx: gradient-text + ai-color-palette', () => {
+    const { stderr } = run(path.join(dir, 'app', 'page.tsx'));
+    expect(stderr).toContain('gradient-text');
+    expect(stderr).toContain('ai-color-palette');
+  });
+
+  test('directory scan shows import context for components', () => {
+    const { stderr } = run(dir);
+    expect(stderr).toContain('imported by page.tsx');
+  });
+
+  test('--json produces clean JSON without framework message', () => {
+    const { stderr, code } = run('--json', dir);
+    expect(code).toBe(2);
+    const parsed = JSON.parse(stderr.trim());
+    expect(parsed).toBeArray();
+    expect(parsed.length).toBeGreaterThanOrEqual(6);
+  });
+});
+
+describe('CLI -- Next.js + CSS Modules project', () => {
+  function run(...args) {
+    const result = spawnSync('node', [SCRIPT, ...args], { encoding: 'utf-8', timeout: 15000 });
+    return { stdout: result.stdout || '', stderr: result.stderr || '', code: result.status };
+  }
+
+  const dir = path.join(FIXTURES, 'framework-next-modules');
+
+  test('finds all expected anti-pattern types', () => {
+    const { code, stderr } = run(dir);
+    expect(code).toBe(2);
+    for (const ap of ['side-tab', 'overused-font', 'pure-black-white', 'layout-transition', 'gradient-text']) {
+      expect(stderr).toContain(ap);
+    }
+  });
+
+  test('StatsCard.module.css: side-tab + overused-font + layout-transition', () => {
+    const { stderr } = run(path.join(dir, 'components', 'StatsCard.module.css'));
+    expect(stderr).toContain('side-tab');
+    expect(stderr).toContain('border-left: 4px solid #6366f1');
+    expect(stderr).toContain('overused-font');
+    expect(stderr).toContain('Inter');
+    expect(stderr).toContain('layout-transition');
+    expect(stderr).toContain('transition: width');
+  });
+
+  test('Sidebar.module.css: side-tab border accent', () => {
+    const { stderr } = run(path.join(dir, 'components', 'Sidebar.module.css'));
+    expect(stderr).toContain('side-tab');
+    expect(stderr).toContain('border-right: 3px solid');
+  });
+
+  test('globals.css: overused Roboto + pure-black-white', () => {
+    const { stderr } = run(path.join(dir, 'app', 'globals.css'));
+    expect(stderr).toContain('overused-font');
+    expect(stderr).toContain('Roboto');
+    expect(stderr).toContain('pure-black-white');
+    expect(stderr).toContain('#000000');
+  });
+
+  test('page.module.css: gradient-text across lines', () => {
+    const { stderr } = run(path.join(dir, 'app', 'page.module.css'));
+    expect(stderr).toContain('gradient-text');
+    expect(stderr).toContain('background-clip: text');
+  });
+
+  test('directory scan shows import context for CSS modules', () => {
+    const { stderr } = run(dir);
+    expect(stderr).toContain('imported by StatsCard.tsx');
+    expect(stderr).toContain('imported by Sidebar.tsx');
+    expect(stderr).toContain('imported by layout.tsx');
+  });
+});
+
+describe('CLI -- Next.js + CSS-in-JS (styled-components) project', () => {
+  function run(...args) {
+    const result = spawnSync('node', [SCRIPT, ...args], { encoding: 'utf-8', timeout: 15000 });
+    return { stdout: result.stdout || '', stderr: result.stderr || '', code: result.status };
+  }
+
+  const dir = path.join(FIXTURES, 'framework-next-cssinjs');
+
+  test('finds all expected anti-pattern types', () => {
+    const { code, stderr } = run(dir);
+    expect(code).toBe(2);
+    for (const ap of ['side-tab', 'gradient-text', 'overused-font', 'bounce-easing', 'pure-black-white', 'layout-transition']) {
+      expect(stderr).toContain(ap);
+    }
+  });
+
+  test('FeatureGrid.tsx: side-tab + bounce-easing + layout-transition', () => {
+    const { stderr } = run(path.join(dir, 'components', 'FeatureGrid.tsx'));
+    expect(stderr).toContain('side-tab');
+    expect(stderr).toContain('border-left: 4px solid');
+    expect(stderr).toContain('bounce-easing');
+    expect(stderr).toContain('animation: bounce');
+    expect(stderr).toContain('layout-transition');
+    expect(stderr).toContain('transition: width');
+  });
+
+  test('Hero.tsx: gradient-text + overused Montserrat font', () => {
+    const { stderr } = run(path.join(dir, 'components', 'Hero.tsx'));
+    expect(stderr).toContain('gradient-text');
+    expect(stderr).toContain('background-clip: text');
+    expect(stderr).toContain('overused-font');
+    expect(stderr).toContain('Montserrat');
+  });
+
+  test('GlobalStyle.tsx: overused Inter + pure-black-white', () => {
+    const { stderr } = run(path.join(dir, 'components', 'GlobalStyle.tsx'));
+    expect(stderr).toContain('overused-font');
+    expect(stderr).toContain('Inter');
+    expect(stderr).toContain('pure-black-white');
+    expect(stderr).toContain('#000000');
+  });
+
+  test('Testimonials.tsx: side-tab + gradient-text in styled blockquote', () => {
+    const { stderr } = run(path.join(dir, 'components', 'Testimonials.tsx'));
+    expect(stderr).toContain('side-tab');
+    expect(stderr).toContain('border-left: 4px solid');
+    expect(stderr).toContain('gradient-text');
+  });
+
+  test('directory scan shows import context for components', () => {
+    const { stderr } = run(dir);
+    expect(stderr).toContain('imported by index.tsx');
+    expect(stderr).toContain('imported by _app.tsx');
+  });
+
+  test('--json produces clean JSON without framework message', () => {
+    const { stderr, code } = run('--json', dir);
+    expect(code).toBe(2);
+    const parsed = JSON.parse(stderr.trim());
+    expect(parsed).toBeArray();
+    expect(parsed.length).toBeGreaterThanOrEqual(6);
+    // Verify importedBy is present in JSON
+    const featureGridFindings = parsed.filter(f => f.file?.includes('FeatureGrid'));
+    expect(featureGridFindings.length).toBeGreaterThan(0);
+    expect(featureGridFindings[0].importedBy).toContain('index.tsx');
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Tier 2: Import graph
+// ---------------------------------------------------------------------------
+
+describe('buildImportGraph', () => {
+  const MF = path.join(FIXTURES, 'multifile');
+
+  test('resolves ES import from tsx to tsx', () => {
+    const graph = buildImportGraph([
+      path.join(MF, 'App.tsx'),
+      path.join(MF, 'Card.tsx'),
+      path.join(MF, 'styles.css'),
+    ]);
+    const appImports = graph.get(path.join(MF, 'App.tsx'));
+    expect(appImports).toBeDefined();
+    expect(appImports.has(path.join(MF, 'Card.tsx'))).toBe(true);
+    expect(appImports.has(path.join(MF, 'styles.css'))).toBe(true);
+  });
+
+  test('resolves extensionless imports', () => {
+    const graph = buildImportGraph([
+      path.join(MF, 'App.tsx'),
+      path.join(MF, 'Card.tsx'),
+    ]);
+    const appImports = graph.get(path.join(MF, 'App.tsx'));
+    expect(appImports.has(path.join(MF, 'Card.tsx'))).toBe(true);
+  });
+
+  test('resolves CSS @import', () => {
+    const graph = buildImportGraph([
+      path.join(MF, 'theme.scss'),
+      path.join(MF, 'variables.scss'),
+    ]);
+    const themeImports = graph.get(path.join(MF, 'theme.scss'));
+    expect(themeImports).toBeDefined();
+    expect(themeImports.has(path.join(MF, 'variables.scss'))).toBe(true);
+  });
+
+  test('ignores bare/node_modules imports', () => {
+    const graph = buildImportGraph([
+      path.join(MF, 'App.tsx'),
+    ]);
+    const appImports = graph.get(path.join(MF, 'App.tsx'));
+    // Should not contain 'react' or 'styled-components'
+    for (const imp of appImports) {
+      expect(imp).toContain(MF);
+    }
+  });
+});
+
+describe('resolveImport', () => {
+  const MF = path.join(FIXTURES, 'multifile');
+
+  test('resolves relative path with extension', () => {
+    const fileSet = new Set([path.join(MF, 'Card.tsx')]);
+    const result = resolveImport('./Card.tsx', MF, fileSet);
+    expect(result).toBe(path.join(MF, 'Card.tsx'));
+  });
+
+  test('resolves extensionless import by trying extensions', () => {
+    const fileSet = new Set([path.join(MF, 'Card.tsx')]);
+    const result = resolveImport('./Card', MF, fileSet);
+    expect(result).toBe(path.join(MF, 'Card.tsx'));
+  });
+
+  test('returns null for bare specifiers', () => {
+    const fileSet = new Set([path.join(MF, 'Card.tsx')]);
+    expect(resolveImport('react', MF, fileSet)).toBeNull();
+    expect(resolveImport('styled-components', MF, fileSet)).toBeNull();
+  });
+
+  test('returns null for unresolvable imports', () => {
+    const fileSet = new Set([path.join(MF, 'Card.tsx')]);
+    expect(resolveImport('./Unknown', MF, fileSet)).toBeNull();
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Tier 2: Multi-file directory scan
+// ---------------------------------------------------------------------------
+
+describe('CLI -- multi-file scan', () => {
+  function run(...args) {
+    const result = spawnSync('node', [SCRIPT, ...args], { encoding: 'utf-8', timeout: 15000 });
+    return { stdout: result.stdout || '', stderr: result.stderr || '', code: result.status };
+  }
+
+  test('scanning multifile/ directory finds findings across files', () => {
+    const { code, stderr } = run(path.join(FIXTURES, 'multifile'));
+    expect(code).toBe(2);
+    expect(stderr).toContain('side-tab');
+  });
+
+  test('--json multi-file scan includes import context', () => {
+    const { stderr, code } = run('--json', path.join(FIXTURES, 'multifile'));
+    expect(code).toBe(2);
+    const parsed = JSON.parse(stderr.trim());
+    expect(parsed.length).toBeGreaterThan(0);
+    // Findings from Card.tsx should mention being imported by App.tsx
+    const cardFindings = parsed.filter(f => f.file?.includes('Card.tsx'));
+    expect(cardFindings.length).toBeGreaterThan(0);
+    expect(cardFindings.some(f => f.importedBy?.includes('App.tsx'))).toBe(true);
+  });
+});
+
+// ---------------------------------------------------------------------------
+// Tier 3: Framework config detection
+// ---------------------------------------------------------------------------
+
+describe('detectFrameworkConfig', () => {
+  test('detects next.config.mjs and returns Next.js with default port', () => {
+    const result = detectFrameworkConfig(path.join(FIXTURES, 'framework-next-tailwind'));
+    expect(result).not.toBeNull();
+    expect(result.name).toBe('Next.js');
+    expect(result.port).toBe(3000);
+  });
+
+  test('detects next.config.js (pages router)', () => {
+    const result = detectFrameworkConfig(path.join(FIXTURES, 'framework-next-cssinjs'));
+    expect(result).not.toBeNull();
+    expect(result.name).toBe('Next.js');
+  });
+
+  test('parses custom port from vite.config.ts', () => {
+    const result = detectFrameworkConfig(path.join(FIXTURES, 'framework-vite'));
+    expect(result).not.toBeNull();
+    expect(result.name).toBe('Vite');
+    expect(result.port).toBe(8080);
+  });
+
+  test('returns null for directory without framework config', () => {
+    const result = detectFrameworkConfig(path.join(FIXTURES, 'multifile'));
+    expect(result).toBeNull();
+  });
+
+  test('returns null for nonexistent directory', () => {
+    const result = detectFrameworkConfig('/nonexistent/path/12345');
+    expect(result).toBeNull();
+  });
+});
+
+describe('isPortListening', () => {
+  test('returns { listening: false } for unlikely port', async () => {
+    const result = await isPortListening(59999);
+    expect(result.listening).toBe(false);
+  });
+});
+
+describe('FRAMEWORK_CONFIGS', () => {
+  test('covers major frameworks', () => {
+    const names = FRAMEWORK_CONFIGS.map(c => c.name);
+    expect(names).toContain('Next.js');
+    expect(names).toContain('Vite');
+    expect(names).toContain('SvelteKit');
+    expect(names).toContain('Nuxt');
+    expect(names).toContain('Astro');
+  });
+
+  test('each config has required fields', () => {
+    for (const cfg of FRAMEWORK_CONFIGS) {
+      expect(cfg.name).toBeTypeOf('string');
+      expect(cfg.defaultPort).toBeTypeOf('number');
+      expect(cfg.files).toBeArray();
+      expect(cfg.files.length).toBeGreaterThan(0);
+    }
+  });
+});
+
+describe('CLI -- dev server suggestion', () => {
+  function run(...args) {
+    const result = spawnSync('node', [SCRIPT, ...args], { encoding: 'utf-8', timeout: 15000 });
+    return { stdout: result.stdout || '', stderr: result.stderr || '', code: result.status };
+  }
+
+  test('suggests URL scan when Next.js config found', () => {
+    const { stderr } = run(path.join(FIXTURES, 'framework-next-tailwind'));
+    expect(stderr).toContain('Next.js');
+    expect(stderr).toContain('3000');
+  });
+
+  test('suggests URL scan when Vite config found', () => {
+    const { stderr } = run(path.join(FIXTURES, 'framework-vite'));
+    expect(stderr).toContain('Vite');
+    expect(stderr).toContain('8080');
+  });
+});

tests/fixtures/antipatterns/color.html 🔗

@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Color Anti-Patterns — Should Flag vs Should Pass</title>
+  <style>
+    body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #1a1a1a; }
+    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }
+    .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
+    .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }
+    .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }
+  </style>
+</head>
+<body>
+  <div class="grid">
+
+    <!-- ════════════════════════════════════════════════════════════
+         LEFT COLUMN: should flag
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="flag">
+      <h2>Should flag</h2>
+
+      <h3>Pure black &amp; white</h3>
+      <div class="card" style="background: #000000; color: white;">
+        <p>Pure #000 background</p>
+      </div>
+      <div class="card" style="background: #ffffff;">
+        <p style="color: #000000;">Pure #000 text on pure #fff background</p>
+      </div>
+
+      <h3>Gray on color</h3>
+      <div class="card" style="background: rgb(59, 130, 246);">
+        <p style="color: rgb(150, 150, 150);">Gray text on blue background</p>
+      </div>
+      <div class="card" style="background: rgb(16, 185, 129);">
+        <p style="color: rgb(180, 180, 180);">Gray text on green background</p>
+      </div>
+
+      <h3>Low contrast</h3>
+      <div class="card" style="background: white;">
+        <p style="color: rgb(200, 200, 200);">Light gray text on white — very low contrast</p>
+      </div>
+      <div class="card" style="background: rgb(30, 30, 30);">
+        <p style="color: rgb(80, 80, 80);">Dark gray text on near-black — low contrast</p>
+      </div>
+      <!-- Gradient background — gray heading is unreadable across every stop -->
+      <div class="card" style="background: linear-gradient(180deg, #3b82f6 0%, #8b5cf6 100%); padding: 16px;">
+        <h1 style="color: #808080; font-size: 32px; font-weight: 800; margin: 0;">Welcome to Our Platform</h1>
+        <p style="color: #666666; font-size: 14px;">The best solution for modern teams and businesses</p>
+      </div>
+
+      <h3>Gradient text</h3>
+      <h3 style="background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 1.5rem;">
+        Gradient Heading
+      </h3>
+
+      <h3>AI color palette</h3>
+      <h1 style="color: rgb(139, 92, 246); font-size: 1.5rem; margin: 0;">Purple heading text</h1>
+
+      <h3>Tailwind color anti-patterns</h3>
+      <div class="bg-black text-white p-4 rounded card" style="background: black; color: white;">
+        <p>bg-black — pure black bg</p>
+      </div>
+      <div class="bg-blue-500 text-gray-400 p-4 rounded card" style="background: rgb(59, 130, 246);">
+        <p style="color: rgb(156, 163, 175);">text-gray-400 on bg-blue-500 — gray on color</p>
+      </div>
+      <h3 class="text-purple-500 text-2xl font-bold" style="color: rgb(168, 85, 247); font-size: 1.5rem; font-weight: 700;">text-purple-500 heading</h3>
+      <div class="bg-gradient-to-r from-purple-500 to-indigo-500 text-white p-4 rounded card" style="background: linear-gradient(to right, rgb(168, 85, 247), rgb(99, 102, 241)); color: white;">
+        <p>Purple-to-indigo gradient</p>
+      </div>
+    </div>
+
+    <!-- ════════════════════════════════════════════════════════════
+         RIGHT COLUMN: should pass
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="pass">
+      <h2>Should pass</h2>
+
+      <h3>Tinted neutrals</h3>
+      <div class="card" style="background: rgb(26, 26, 30);">
+        <p style="color: rgb(240, 240, 245);">Near-black bg, near-white text — tinted, not pure</p>
+      </div>
+      <div class="card" style="background: rgb(250, 250, 252);">
+        <p style="color: rgb(20, 20, 25);">Near-white bg, near-black text — good contrast, tinted</p>
+      </div>
+
+      <h3>Good contrast</h3>
+      <div class="card" style="background: rgb(30, 64, 175);">
+        <p style="color: rgb(240, 240, 245);">Near-white text on dark blue — high contrast, not pure white</p>
+      </div>
+      <div class="card" style="background: rgb(16, 185, 129);">
+        <p style="color: rgb(5, 46, 32);">Dark green text on green bg — same hue family</p>
+      </div>
+
+      <h3>Distinctive accents (not AI purple)</h3>
+      <h3 style="color: rgb(220, 38, 38); font-size: 1.25rem; margin: 0;">Red heading — not AI purple</h3>
+      <h3 style="color: rgb(180, 83, 9); font-size: 1.25rem; margin: 8px 0 0;">Amber heading — distinctive</h3>
+
+      <h3>Background-image url() ancestor (not low contrast)</h3>
+      <!-- White text on transparent bg where ancestor has a background-image.
+           The detector cannot determine the image color, so it must NOT assume
+           white and report a false low-contrast finding. -->
+      <div data-test="bg-image-ancestor" style="background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg=='); background-size: cover; padding: 20px;">
+        <p style="color: rgb(255, 255, 255); font-size: 16px;">White text on image background — should not flag low-contrast</p>
+      </div>
+
+      <h3>Tailwind opacity-modified bg-black (not pure black)</h3>
+      <!-- bg-black/N classes are NOT pure black — they apply an alpha modifier.
+           The detector must not match these as pure-black-white. -->
+      <div class="bg-black/3 p-4 rounded card" data-test="bg-black-opacity" style="background: rgba(0,0,0,0.03);">
+        <p>bg-black/3 — 3% opacity, not pure black</p>
+      </div>
+      <div class="hover:bg-black/5 p-4 rounded card" data-test="bg-black-hover-opacity" style="background: rgba(0,0,0,0);">
+        <p>hover:bg-black/5 — hover state, not pure black</p>
+      </div>
+      <div class="bg-black/50 p-4 rounded card" data-test="bg-black-half-opacity" style="background: rgba(0,0,0,0.5);">
+        <p style="color: white;">bg-black/50 — 50% opacity, not pure black</p>
+      </div>
+
+      <h3>Emoji on light backgrounds</h3>
+      <!-- Emojis render as multicolor glyphs regardless of CSS color, so the
+           CSS color is irrelevant for contrast. These should NOT be flagged. -->
+      <div class="card emoji-test" data-test="emoji-light" style="background: #ffe4e6;">
+        <p style="color: #ffe4e6; font-size: 24px;">⚠️ 🚨 ✨ 🎨</p>
+      </div>
+      <div class="card emoji-test" data-test="emoji-dark" style="background: #1a1a1a;">
+        <p style="color: #1a1a1a; font-size: 24px;">⚠️ 🚨 ✨ 🎨</p>
+      </div>
+    </div>
+  </div>
+  <script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/cramped-padding.html 🔗

@@ -0,0 +1,214 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Cramped Padding — Should Flag vs Should Pass</title>
+  <style>
+    /* Comprehensive fixture for the cramped-padding rule.
+       Both columns include a wide variety of cases so we can verify the
+       rule scales sensibly with font-size and treats the two axes
+       independently (vertical can be tighter because line-height already
+       provides built-in breathing room; horizontal has no equivalent). */
+    body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.5; }
+    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1400px; margin: 0 auto; }
+    .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
+    .col h3 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; margin: 28px 0 10px; color: #64748b; }
+    .case { margin-bottom: 14px; padding: 14px 16px; background: white; border: 1px solid #e2e8f0; border-radius: 10px; }
+    .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 8px; font-style: italic; }
+
+    /* ── FLAG: cramped body in cards ── */
+    .flag-card-4 { padding: 4px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; }
+    .flag-card-2 { padding: 2px; background: #f1f5f9; border-radius: 6px; font-size: 14px; }
+    .flag-card-bg4 { padding: 4px; background: #fef3c7; border-radius: 6px; font-size: 16px; }
+
+    /* ── FLAG: asymmetric cramping ── */
+    .flag-asym-v { padding: 1px 16px; background: #f1f5f9; border-radius: 6px; font-size: 14px; }
+    .flag-asym-h { padding: 12px 4px; background: #f1f5f9; border-radius: 6px; font-size: 14px; }
+
+    /* ── FLAG: cramped large text (currently MISSED by the 8px floor) ── */
+    .flag-heading-tight { padding: 8px; background: #f1f5f9; border-radius: 6px; font-size: 24px; font-weight: 600; line-height: 1.2; }
+    .flag-hero-tight { padding: 6px 16px; background: #f1f5f9; border-radius: 6px; font-size: 32px; font-weight: 700; line-height: 1.2; }
+
+    /* ── FLAG: cramped multi-line code block ── */
+    .flag-pre-tight { padding: 2px; background: #1e293b; color: #e2e8f0; border-radius: 6px; font-family: ui-monospace, monospace; font-size: 14px; line-height: 1.5; }
+
+    /* ── PASS: small inline pills (the .detection-cmd category) ── */
+    .pass-cmd {
+      display: inline-block;
+      font-family: ui-monospace, SFMono-Regular, monospace;
+      font-size: 13px;
+      line-height: 1.625;
+      color: #0f172a;
+      background: #f5f5f7;
+      padding: 6px 14px;
+      border-radius: 6px;
+      white-space: nowrap;
+    }
+    .pass-cmd::before { content: '➜ '; color: #ec4899; }
+    .pass-badge {
+      display: inline-block;
+      font-size: 11px;
+      line-height: 1.6;
+      padding: 4px 8px;
+      background: #dbeafe;
+      color: #1e40af;
+      border-radius: 999px;
+      font-weight: 600;
+    }
+    .pass-chip {
+      display: inline-block;
+      font-size: 12px;
+      line-height: 1.6;
+      padding: 4px 10px;
+      background: #fef3c7;
+      color: #92400e;
+      border-radius: 4px;
+      font-weight: 500;
+    }
+
+    /* ── PASS: cards at current standards ── */
+    .pass-card-min { padding: 8px; background: #f1f5f9; border-radius: 6px; font-size: 16px; line-height: 1.6; }
+    .pass-card-good { padding: 16px; background: #f1f5f9; border-radius: 8px; font-size: 14px; line-height: 1.6; }
+    .pass-card-large { padding: 12px; background: #f1f5f9; border-radius: 8px; font-size: 18px; line-height: 1.6; }
+
+    /* ── PASS: comfortable code blocks ── */
+    .pass-pre-min { padding: 8px; background: #1e293b; color: #e2e8f0; border-radius: 6px; font-family: ui-monospace, monospace; font-size: 14px; line-height: 1.5; }
+    .pass-pre-good { padding: 12px; background: #1e293b; color: #e2e8f0; border-radius: 6px; font-family: ui-monospace, monospace; font-size: 14px; line-height: 1.5; }
+
+    /* ── PASS: interactive elements ── */
+    .pass-button-std { padding: 8px 16px; background: #3b82f6; color: white; border: 0; border-radius: 6px; font-size: 14px; cursor: pointer; }
+    .pass-input-std { padding: 8px 12px; border: 1px solid #cbd5e1; border-radius: 4px; font-size: 16px; width: 100%; box-sizing: border-box; }
+
+    /* ── PASS: big text with proportional padding ── */
+    .pass-heading-good { padding: 16px; background: #f1f5f9; border-radius: 8px; font-size: 24px; font-weight: 600; line-height: 1.2; }
+    .pass-hero-good { padding: 20px 32px; background: #f1f5f9; border-radius: 8px; font-size: 32px; font-weight: 700; line-height: 1.2; }
+  </style>
+</head>
+<body>
+  <div class="grid">
+
+    <!-- ════════════════════════════════════════════════════════════
+         LEFT COLUMN: should flag
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="flag">
+      <h2>Should flag</h2>
+
+      <h3>Cramped body in cards</h3>
+      <div class="case">
+        <span class="case-label">14px body, 4px padding all sides</span>
+        <div class="flag-card-4">This text is crammed against the border with only 4px padding. It feels claustrophobic and hard to read because the eye has nowhere to rest.</div>
+      </div>
+      <div class="case">
+        <span class="case-label">14px body, 2px padding all sides</span>
+        <div class="flag-card-2">Two pixels of padding is essentially nothing — body text needs room to breathe inside its container, especially across multiple lines.</div>
+      </div>
+      <div class="case">
+        <span class="case-label">16px body, 4px padding, background only</span>
+        <div class="flag-card-bg4">A colored background containment with only 4px padding around 16px body text. The colored edge feels like it's pressing in.</div>
+      </div>
+
+      <h3>Asymmetric cramping</h3>
+      <div class="case">
+        <span class="case-label">1px vertical / 16px horizontal, 14px body</span>
+        <div class="flag-asym-v">Generous horizontal padding doesn't save you when the vertical padding is just one pixel. Lines stack on top of each other against the container edge.</div>
+      </div>
+      <div class="case">
+        <span class="case-label">12px vertical / 4px horizontal, 14px body</span>
+        <div class="flag-asym-h">Comfortable vertical padding can't fix horizontal text running right up against the side edges. Words feel like they're falling off the container.</div>
+      </div>
+
+      <h3>Currently missed: cramped large text</h3>
+      <div class="case">
+        <span class="case-label">24px heading, 8px padding (passes the 8px floor but is too tight for the text size)</span>
+        <div class="flag-heading-tight">Tight Heading Container Label</div>
+      </div>
+      <div class="case">
+        <span class="case-label">32px hero, 6px vertical / 16px horizontal padding</span>
+        <div class="flag-hero-tight">Hero Section With Tight Vertical Padding</div>
+      </div>
+
+      <h3>Cramped multi-line code block</h3>
+      <div class="case">
+        <span class="case-label">&lt;pre&gt;, 2px padding, 14px monospace</span>
+        <pre class="flag-pre-tight">function example() {
+  const value = "cramped";
+  return value;
+}</pre>
+      </div>
+    </div>
+
+    <!-- ════════════════════════════════════════════════════════════
+         RIGHT COLUMN: should pass
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="pass">
+      <h2>Should pass</h2>
+
+      <h3>Small inline pills</h3>
+      <div class="case">
+        <span class="case-label">.detection-cmd replica: 13px font, 6px / 14px padding (the homepage case)</span>
+        <code class="pass-cmd">npx impeccable detect src/</code>
+      </div>
+      <div class="case">
+        <span class="case-label">small badge: 11px font, 4px / 8px padding</span>
+        <span class="pass-badge">FEATURED ITEM BADGE</span>
+      </div>
+      <div class="case">
+        <span class="case-label">tag chip: 12px font, 4px / 10px padding</span>
+        <span class="pass-chip">design system tag chip</span>
+      </div>
+
+      <h3>Cards at current standards</h3>
+      <div class="case">
+        <span class="case-label">16px body, 8px padding (the original 8px floor)</span>
+        <div class="pass-card-min">Standard card with 8px padding around 16px body text. Comfortable and within current conventions.</div>
+      </div>
+      <div class="case">
+        <span class="case-label">14px body, 16px padding (generous)</span>
+        <div class="pass-card-good">Generous 16px padding around 14px body text. The eye has plenty of room from the container edge.</div>
+      </div>
+      <div class="case">
+        <span class="case-label">18px body, 12px padding (proportional)</span>
+        <div class="pass-card-large">Larger body text gets proportionally more padding. 12px around 18px text feels balanced.</div>
+      </div>
+
+      <h3>Comfortable code blocks</h3>
+      <div class="case">
+        <span class="case-label">multi-line &lt;pre&gt;, 8px padding, 14px font</span>
+        <pre class="pass-pre-min">function example() {
+  const value = "ok";
+  return value;
+}</pre>
+      </div>
+      <div class="case">
+        <span class="case-label">multi-line &lt;pre&gt;, 12px padding, 14px font</span>
+        <pre class="pass-pre-good">function example() {
+  const value = "comfortable";
+  return value;
+}</pre>
+      </div>
+
+      <h3>Interactive elements</h3>
+      <div class="case">
+        <span class="case-label">button: 14px font, 8px / 16px padding</span>
+        <button class="pass-button-std">Standard sized button</button>
+      </div>
+      <div class="case">
+        <span class="case-label">form input: 16px font, 8px / 12px padding</span>
+        <input type="text" class="pass-input-std" value="Standard input with adequate padding">
+      </div>
+
+      <h3>Big text with proportional padding</h3>
+      <div class="case">
+        <span class="case-label">24px heading, 16px padding</span>
+        <div class="pass-heading-good">Heading With Room To Breathe</div>
+      </div>
+      <div class="case">
+        <span class="case-label">32px hero, 20px / 32px padding</span>
+        <div class="pass-hero-good">Hero With Big Padding</div>
+      </div>
+    </div>
+  </div>
+  <script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/cssinjs-should-flag.tsx 🔗

@@ -0,0 +1,44 @@
+// CSS-in-JS patterns with anti-patterns (styled-components + emotion)
+
+import styled from 'styled-components';
+import { css } from '@emotion/react';
+
+// styled-components: side-tab + border-accent-on-rounded
+export const Card = styled.div`
+  border-left: 4px solid #3b82f6;
+  border-radius: 12px;
+  padding: 24px;
+  font-family: 'Inter', sans-serif;
+`;
+
+// styled-components: pure black background + gradient text
+export const Hero = styled.section`
+  background-color: #000000;
+  padding: 80px 20px;
+  text-align: center;
+
+  h1 {
+    background: linear-gradient(to right, #a855f7, #06b6d4);
+    -webkit-background-clip: text;
+    background-clip: text;
+    color: transparent;
+  }
+`;
+
+// emotion css: bounce animation + layout transition
+export const animatedPanel = css`
+  animation: bounce 1s infinite;
+  transition: width 0.3s ease;
+`;
+
+// styled with parenthesized component
+export const AccentBox = styled(Box)`
+  border-right: 5px solid #8b5cf6;
+  border-radius: 8px;
+`;
+
+// Object style pattern
+export const inlineStyles = {
+  borderLeft: '4px solid #6366f1',
+  borderRadius: '12px',
+};

tests/fixtures/antipatterns/cssinjs-should-pass.tsx 🔗

@@ -0,0 +1,25 @@
+// Clean CSS-in-JS patterns -- no anti-patterns
+
+import styled from 'styled-components';
+import { css } from '@emotion/react';
+
+export const Card = styled.div`
+  border-radius: 12px;
+  padding: 24px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  font-family: 'Geist', system-ui, sans-serif;
+`;
+
+export const Hero = styled.section`
+  background-color: #0f172a;
+  padding: 80px 20px;
+
+  h1 {
+    color: #f8fafc;
+    font-size: 3rem;
+  }
+`;
+
+export const smoothPanel = css`
+  transition: opacity 0.3s ease, transform 0.2s ease;
+`;

tests/fixtures/antipatterns/external-styles.css 🔗

@@ -0,0 +1,33 @@
+/* External stylesheet that applies anti-pattern styles */
+
+/* Side-tab via external class */
+.external-side-tab {
+  background: white;
+  padding: 1rem;
+  border-radius: 12px;
+  border-left: 4px solid #3b82f6;
+  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+}
+
+/* Top border accent via external class */
+.external-top-accent {
+  background: white;
+  padding: 1rem;
+  border-radius: 12px;
+  border-top: 3px solid #8b5cf6;
+  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+}
+
+/* Overused font from external stylesheet */
+.external-inter {
+  font-family: 'Inter', sans-serif;
+}
+
+/* Clean card — should NOT flag */
+.external-clean {
+  background: white;
+  padding: 1rem;
+  border-radius: 12px;
+  border: 1px solid #e5e7eb;
+  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+}

tests/fixtures/antipatterns/framework-next-cssinjs/components/FeatureGrid.tsx 🔗

@@ -0,0 +1,58 @@
+import styled from "styled-components";
+
+const Grid = styled.div`
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 32px;
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 80px 24px;
+`;
+
+const Card = styled.div`
+  border-left: 4px solid #8b5cf6;
+  border-radius: 16px;
+  background: #1a1a2e;
+  padding: 32px;
+  box-shadow: 0 0 25px rgba(139, 92, 246, 0.2);
+  transition: width 0.3s ease;
+`;
+
+const CardIcon = styled.div`
+  font-size: 40px;
+  margin-bottom: 16px;
+  animation: bounce 2s infinite;
+`;
+
+const CardTitle = styled.h3`
+  font-size: 20px;
+  font-weight: 700;
+  color: #a855f7;
+  margin-bottom: 8px;
+`;
+
+const CardDescription = styled.p`
+  font-size: 15px;
+  color: #6b7280;
+  line-height: 1.6;
+`;
+
+const features = [
+  { icon: "⚡", title: "Blazing Fast", description: "Optimized for speed with edge-first architecture." },
+  { icon: "🔒", title: "Secure by Default", description: "Enterprise-grade security with zero configuration." },
+  { icon: "📦", title: "Modular Design", description: "Pick and choose only what you need. No bloat." },
+];
+
+export function FeatureGrid() {
+  return (
+    <Grid>
+      {features.map((feature) => (
+        <Card key={feature.title}>
+          <CardIcon>{feature.icon}</CardIcon>
+          <CardTitle>{feature.title}</CardTitle>
+          <CardDescription>{feature.description}</CardDescription>
+        </Card>
+      ))}
+    </Grid>
+  );
+}

tests/fixtures/antipatterns/framework-next-cssinjs/components/GlobalStyle.tsx 🔗

@@ -0,0 +1,18 @@
+import { createGlobalStyle } from "styled-components";
+
+export const GlobalStyle = createGlobalStyle`
+  *,
+  *::before,
+  *::after {
+    box-sizing: border-box;
+    margin: 0;
+    padding: 0;
+  }
+
+  body {
+    font-family: 'Inter', sans-serif;
+    background-color: #000000;
+    color: #ffffff;
+    line-height: 1.6;
+  }
+`;

tests/fixtures/antipatterns/framework-next-cssinjs/components/Hero.tsx 🔗

@@ -0,0 +1,61 @@
+import styled from "styled-components";
+
+const Section = styled.section`
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 80px 24px;
+  text-align: center;
+`;
+
+const Title = styled.h1`
+  font-size: 64px;
+  font-weight: 800;
+  font-family: 'Montserrat', sans-serif;
+  background: linear-gradient(135deg, #a855f7, #06b6d4);
+  -webkit-background-clip: text;
+  background-clip: text;
+  color: transparent;
+  margin-bottom: 24px;
+  line-height: 1.1;
+`;
+
+const Subtitle = styled.p`
+  font-size: 20px;
+  color: #6b7280;
+  max-width: 600px;
+  margin-bottom: 48px;
+`;
+
+const CTAButton = styled.button`
+  padding: 16px 48px;
+  font-size: 18px;
+  font-weight: 600;
+  color: white;
+  background: linear-gradient(135deg, #8b5cf6, #6366f1);
+  border: none;
+  border-radius: 12px;
+  cursor: pointer;
+  box-shadow: 0 0 40px rgba(139, 92, 246, 0.4);
+  transition: transform 0.2s ease, box-shadow 0.2s ease;
+
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 0 60px rgba(139, 92, 246, 0.6);
+  }
+`;
+
+export function Hero() {
+  return (
+    <Section>
+      <Title>Build the Future</Title>
+      <Subtitle>
+        The most powerful platform for modern web development.
+        Ship faster, scale easier.
+      </Subtitle>
+      <CTAButton>Get Started Free</CTAButton>
+    </Section>
+  );
+}

tests/fixtures/antipatterns/framework-next-cssinjs/components/Testimonials.tsx 🔗

@@ -0,0 +1,53 @@
+import styled from "styled-components";
+
+const Section = styled.section`
+  padding: 80px 24px;
+  max-width: 800px;
+  margin: 0 auto;
+`;
+
+const SectionTitle = styled.h2`
+  font-size: 36px;
+  font-weight: 700;
+  text-align: center;
+  margin-bottom: 48px;
+  background: linear-gradient(to right, #a855f7, #ec4899);
+  -webkit-background-clip: text;
+  background-clip: text;
+  color: transparent;
+`;
+
+const Quote = styled.blockquote`
+  border-left: 4px solid #6366f1;
+  border-radius: 12px;
+  background: #1a1a2e;
+  padding: 24px 32px;
+  margin-bottom: 24px;
+  font-size: 16px;
+  color: #d1d5db;
+  font-style: italic;
+`;
+
+const Author = styled.p`
+  font-size: 14px;
+  color: #6b7280;
+  margin-top: 12px;
+  font-style: normal;
+`;
+
+export function Testimonials() {
+  return (
+    <Section>
+      <SectionTitle>What People Say</SectionTitle>
+      <Quote>
+        "This platform completely transformed our workflow. Deployment went from
+        hours to minutes."
+        <Author>-- Jane Smith, CTO at TechCorp</Author>
+      </Quote>
+      <Quote>
+        "The developer experience is unmatched. I can't imagine going back."
+        <Author>-- Alex Chen, Senior Engineer</Author>
+      </Quote>
+    </Section>
+  );
+}

tests/fixtures/antipatterns/framework-next-cssinjs/pages/_app.tsx 🔗

@@ -0,0 +1,27 @@
+import type { AppProps } from "next/app";
+import { ThemeProvider } from "styled-components";
+import { GlobalStyle } from "../components/GlobalStyle";
+
+const theme = {
+  colors: {
+    primary: "#8b5cf6",
+    secondary: "#06b6d4",
+    background: "#000000",
+    surface: "#1a1a2e",
+    text: "#ffffff",
+    muted: "#6b7280",
+  },
+  fonts: {
+    body: "'Inter', sans-serif",
+    heading: "'Montserrat', sans-serif",
+  },
+};
+
+export default function App({ Component, pageProps }: AppProps) {
+  return (
+    <ThemeProvider theme={theme}>
+      <GlobalStyle />
+      <Component {...pageProps} />
+    </ThemeProvider>
+  );
+}

tests/fixtures/antipatterns/framework-next-cssinjs/pages/index.tsx 🔗

@@ -0,0 +1,13 @@
+import { Hero } from "../components/Hero";
+import { FeatureGrid } from "../components/FeatureGrid";
+import { Testimonials } from "../components/Testimonials";
+
+export default function Home() {
+  return (
+    <>
+      <Hero />
+      <FeatureGrid />
+      <Testimonials />
+    </>
+  );
+}

tests/fixtures/antipatterns/framework-next-modules/app/layout.tsx 🔗

@@ -0,0 +1,19 @@
+import type { Metadata } from "next";
+import "./globals.css";
+
+export const metadata: Metadata = {
+  title: "Dashboard",
+  description: "Analytics dashboard",
+};
+
+export default function RootLayout({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  return (
+    <html lang="en">
+      <body>{children}</body>
+    </html>
+  );
+}

tests/fixtures/antipatterns/framework-next-modules/app/page.module.css 🔗

@@ -0,0 +1,25 @@
+.container {
+  display: flex;
+  min-height: 100vh;
+}
+
+.main {
+  flex: 1;
+  padding: 32px;
+}
+
+.title {
+  font-size: 28px;
+  font-weight: 700;
+  margin-bottom: 32px;
+  background: linear-gradient(to right, #a855f7, #06b6d4);
+  -webkit-background-clip: text;
+  background-clip: text;
+  color: transparent;
+}
+
+.grid {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 24px;
+}

tests/fixtures/antipatterns/framework-next-modules/app/page.tsx 🔗

@@ -0,0 +1,19 @@
+import { StatsCard } from "../components/StatsCard";
+import { Sidebar } from "../components/Sidebar";
+import styles from "./page.module.css";
+
+export default function Dashboard() {
+  return (
+    <div className={styles.container}>
+      <Sidebar />
+      <main className={styles.main}>
+        <h1 className={styles.title}>Dashboard</h1>
+        <div className={styles.grid}>
+          <StatsCard label="Revenue" value="$48,290" change="+12.5%" />
+          <StatsCard label="Users" value="2,847" change="+8.1%" />
+          <StatsCard label="Orders" value="1,024" change="-2.3%" />
+        </div>
+      </main>
+    </div>
+  );
+}

tests/fixtures/antipatterns/framework-next-modules/components/Sidebar.module.css 🔗

@@ -0,0 +1,38 @@
+.sidebar {
+  width: 240px;
+  background: #111827;
+  border-right: 3px solid #4f46e5;
+  padding: 24px 16px;
+  display: flex;
+  flex-direction: column;
+  gap: 32px;
+}
+
+.logo {
+  font-size: 20px;
+  font-weight: 700;
+  color: #fff;
+  padding: 0 8px;
+}
+
+.nav {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.navItem {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 10px 12px;
+  border-radius: 8px;
+  color: #9ca3af;
+  font-size: 14px;
+  transition: background 0.2s ease;
+}
+
+.navItem:hover {
+  background: rgba(255, 255, 255, 0.05);
+  color: #fff;
+}

tests/fixtures/antipatterns/framework-next-modules/components/Sidebar.tsx 🔗

@@ -0,0 +1,24 @@
+import styles from "./Sidebar.module.css";
+
+const navItems = [
+  { label: "Overview", icon: "📊" },
+  { label: "Analytics", icon: "📈" },
+  { label: "Customers", icon: "👥" },
+  { label: "Settings", icon: "⚙️" },
+];
+
+export function Sidebar() {
+  return (
+    <aside className={styles.sidebar}>
+      <div className={styles.logo}>Dashboard</div>
+      <nav className={styles.nav}>
+        {navItems.map((item) => (
+          <a key={item.label} href="#" className={styles.navItem}>
+            <span>{item.icon}</span>
+            <span>{item.label}</span>
+          </a>
+        ))}
+      </nav>
+    </aside>
+  );
+}

tests/fixtures/antipatterns/framework-next-modules/components/StatsCard.module.css 🔗

@@ -0,0 +1,34 @@
+.card {
+  border-left: 4px solid #6366f1;
+  border-radius: 12px;
+  background: #1e1e2e;
+  padding: 24px;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  box-shadow: 0 0 30px rgba(99, 102, 241, 0.3);
+  transition: width 0.3s ease;
+}
+
+.label {
+  font-size: 13px;
+  color: #9ca3af;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+}
+
+.value {
+  font-size: 32px;
+  font-weight: 700;
+  font-family: 'Inter', sans-serif;
+}
+
+.changeUp {
+  color: #22c55e;
+  font-size: 14px;
+}
+
+.changeDown {
+  color: #ef4444;
+  font-size: 14px;
+}

tests/fixtures/antipatterns/framework-next-modules/components/StatsCard.tsx 🔗

@@ -0,0 +1,21 @@
+import styles from "./StatsCard.module.css";
+
+interface StatsCardProps {
+  label: string;
+  value: string;
+  change: string;
+}
+
+export function StatsCard({ label, value, change }: StatsCardProps) {
+  const isPositive = change.startsWith("+");
+
+  return (
+    <div className={styles.card}>
+      <span className={styles.label}>{label}</span>
+      <span className={styles.value}>{value}</span>
+      <span className={isPositive ? styles.changeUp : styles.changeDown}>
+        {change}
+      </span>
+    </div>
+  );
+}

tests/fixtures/antipatterns/framework-next-tailwind/app/globals.css 🔗

@@ -0,0 +1,20 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+  --foreground-rgb: 0, 0, 0;
+  --background-start-rgb: 214, 219, 220;
+  --background-end-rgb: 255, 255, 255;
+}
+
+body {
+  color: rgb(var(--foreground-rgb));
+  background: linear-gradient(
+      to bottom,
+      transparent,
+      rgb(var(--background-end-rgb))
+    )
+    rgb(var(--background-start-rgb));
+  font-family: 'Inter', sans-serif;
+}

tests/fixtures/antipatterns/framework-next-tailwind/app/layout.tsx 🔗

@@ -0,0 +1,22 @@
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import "./globals.css";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+  title: "Create Next App",
+  description: "Generated by create next app",
+};
+
+export default function RootLayout({
+  children,
+}: Readonly<{
+  children: React.ReactNode;
+}>) {
+  return (
+    <html lang="en">
+      <body className={inter.className}>{children}</body>
+    </html>
+  );
+}

tests/fixtures/antipatterns/framework-next-tailwind/app/page.tsx 🔗

@@ -0,0 +1,50 @@
+import { FeatureCard } from "../components/FeatureCard";
+import { PricingCard } from "../components/PricingCard";
+
+export default function Home() {
+  return (
+    <main className="flex min-h-screen flex-col items-center justify-between p-24">
+      <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
+        <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4">
+          Get started by editing&nbsp;
+          <code className="font-mono font-bold">app/page.tsx</code>
+        </p>
+      </div>
+
+      <div className="mb-32 text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:text-left">
+        <h1 className="text-5xl font-bold bg-gradient-to-r from-purple-400 to-cyan-400 bg-clip-text text-transparent mb-8">
+          Welcome to Our Platform
+        </h1>
+        <p className="text-gray-400 text-lg mb-12">
+          The next generation of web development
+        </p>
+
+        <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
+          <FeatureCard
+            title="Fast"
+            description="Lightning-fast performance out of the box"
+            icon="⚡"
+          />
+          <FeatureCard
+            title="Scalable"
+            description="Grows with your business needs"
+            icon="📈"
+          />
+          <FeatureCard
+            title="Secure"
+            description="Enterprise-grade security built in"
+            icon="🔒"
+          />
+        </div>
+
+        <div className="mt-16">
+          <h2 className="text-purple-500 text-3xl font-bold text-center mb-8">Pricing</h2>
+          <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
+            <PricingCard name="Starter" price="$9" features={["5 projects", "Basic support"]} />
+            <PricingCard name="Pro" price="$29" features={["Unlimited projects", "Priority support"]} highlighted />
+          </div>
+        </div>
+      </div>
+    </main>
+  );
+}

tests/fixtures/antipatterns/framework-next-tailwind/components/FeatureCard.tsx 🔗

@@ -0,0 +1,15 @@
+interface FeatureCardProps {
+  title: string;
+  description: string;
+  icon: string;
+}
+
+export function FeatureCard({ title, description, icon }: FeatureCardProps) {
+  return (
+    <div className="group border-l-4 border-blue-500 rounded-xl bg-white p-6 shadow-lg hover:shadow-xl transition-all">
+      <div className="text-4xl mb-4 animate-bounce">{icon}</div>
+      <h3 className="text-purple-600 text-xl font-bold mb-2">{title}</h3>
+      <p className="text-gray-400">{description}</p>
+    </div>
+  );
+}

tests/fixtures/antipatterns/framework-next-tailwind/components/PricingCard.tsx 🔗

@@ -0,0 +1,35 @@
+interface PricingCardProps {
+  name: string;
+  price: string;
+  features: string[];
+  highlighted?: boolean;
+}
+
+export function PricingCard({ name, price, features, highlighted }: PricingCardProps) {
+  return (
+    <div
+      className={`rounded-2xl p-8 ${
+        highlighted
+          ? "bg-black border-t-4 border-violet-500 text-white"
+          : "bg-white border border-gray-200"
+      }`}
+    >
+      <h3 className="text-xl font-bold mb-2">{name}</h3>
+      <div className="text-4xl font-bold mb-6 bg-gradient-to-r from-violet-500 to-fuchsia-500 bg-clip-text text-transparent">
+        {price}
+        <span className="text-sm text-gray-400">/mo</span>
+      </div>
+      <ul className="space-y-3">
+        {features.map((feature) => (
+          <li key={feature} className="flex items-center gap-2">
+            <span className="text-violet-500">✓</span>
+            {feature}
+          </li>
+        ))}
+      </ul>
+      <button className="mt-8 w-full py-3 rounded-lg bg-gradient-to-r from-purple-500 to-indigo-500 text-white font-semibold transition-all hover:scale-105">
+        Get Started
+      </button>
+    </div>
+  );
+}

tests/fixtures/antipatterns/framework-next-tailwind/tailwind.config.ts 🔗

@@ -0,0 +1,20 @@
+import type { Config } from "tailwindcss";
+
+const config: Config = {
+  content: [
+    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
+    "./components/**/*.{js,ts,jsx,tsx,mdx}",
+    "./app/**/*.{js,ts,jsx,tsx,mdx}",
+  ],
+  theme: {
+    extend: {
+      backgroundImage: {
+        "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
+        "gradient-conic":
+          "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
+      },
+    },
+  },
+  plugins: [],
+};
+export default config;

tests/fixtures/antipatterns/glow.html 🔗

@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Dark Glow Anti-Patterns — Should Flag vs Should Pass</title>
+  <style>
+    /* Two-column fixture: left = should flag, right = should pass.
+       The dark-glow rule fires when a colored box-shadow appears on a
+       child whose ancestor has a dark background. Each column wraps
+       its dark-context cases in its own .dark-context section so the
+       parent-bg lookup behaves predictably regardless of layout. */
+    body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }
+    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }
+    .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
+    .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }
+    .dark-context { background: #111827; padding: 16px; border-radius: 12px; }
+    .dark-context + .dark-context { margin-top: 12px; }
+    /* Card variants: explicit dark vs light pairings, both WCAG AA compliant */
+    .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }
+    .card h4 { font-weight: 600; font-size: 13px; margin: 0; }
+    .card p { font-size: 12px; margin: 4px 0 0; }
+    .card-dark { background: #1f2937; }
+    .card-dark h4 { color: #f9fafb; }
+    .card-dark p { color: #cbd5e1; }
+    .card-light { background: white; }
+    .card-light h4 { color: #0f172a; }
+    .card-light p { color: #475569; }
+
+    /* ── FLAG: dark background + colored glow ── */
+    .glow-blue { box-shadow: 0 0 20px rgba(59, 130, 246, 0.4); }
+    .glow-purple { box-shadow: 0 0 25px rgba(139, 92, 246, 0.35); }
+    .glow-cyan { box-shadow: 0 0 15px rgba(6, 182, 212, 0.5); }
+    .glow-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 0 30px rgba(168, 85, 247, 0.3); }
+
+    /* ── PASS: same dark/light backgrounds, but neutral or no glow ── */
+    .light-colored-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.15); }
+    .dark-normal-shadow { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); }
+    .dark-focus-ring { box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5); }
+    .dark-subtle-shadow { box-shadow: 0 1px 3px rgba(59, 130, 246, 0.2); }
+    .dark-elevated-sm { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); }
+    .dark-elevated-md { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4); }
+    .dark-elevated-lg { box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5); }
+    .dark-elevated-inset { box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3); }
+    .dark-elevated-multi { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2); }
+    .elevated-sm-light { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }
+    .elevated-md-light { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }
+    .elevated-warm-light { box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.15); }
+    /* Medium-gray card: lighter than dark mode, used to demonstrate "not dark enough" for glow detection. White text for AA contrast. */
+    .card-medium { background: #6b7280; }
+    .card-medium h4 { color: #ffffff; }
+    .card-medium p { color: #ffffff; }
+    .medium-gray-shadow { box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3); }
+  </style>
+</head>
+<body>
+  <div class="grid">
+    <!-- ════════════════════════════════════════════════════════════
+         LEFT COLUMN: should flag (dark page + colored glows)
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="flag">
+      <h2>Should flag</h2>
+
+      <h3>CSS colored glows on dark background</h3>
+      <div class="dark-context">
+        <div class="card card-dark glow-blue">
+          <h4>Blue glow</h4>
+          <p>box-shadow: 0 0 20px rgba(59, 130, 246, 0.4)</p>
+        </div>
+        <div class="card card-dark glow-purple">
+          <h4>Purple glow</h4>
+          <p>box-shadow: 0 0 25px rgba(139, 92, 246, 0.35)</p>
+        </div>
+        <div class="card card-dark glow-cyan">
+          <h4>Cyan glow</h4>
+          <p>box-shadow: 0 0 15px rgba(6, 182, 212, 0.5)</p>
+        </div>
+        <div class="card card-dark glow-multi">
+          <h4>Multi-shadow with colored glow</h4>
+          <p>Normal shadow + purple glow combined.</p>
+        </div>
+      </div>
+
+      <h3>Inline-style glow (regex path)</h3>
+      <div class="dark-context">
+        <div class="card card-dark" style="box-shadow: 0 0 20px rgba(236, 72, 153, 0.5);">
+          <h4>Inline pink glow</h4>
+          <p>Inline style for regex detection path.</p>
+        </div>
+      </div>
+    </div>
+
+    <!-- ════════════════════════════════════════════════════════════
+         RIGHT COLUMN: should pass
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="pass">
+      <h2>Should pass</h2>
+
+      <h3>Light page + colored shadow</h3>
+      <div class="card card-light light-colored-shadow">
+        <h4>Colored shadow on light background</h4>
+        <p>Not dark mode, colored shadow is fine.</p>
+      </div>
+
+      <h3>Dark cards with neutral shadows</h3>
+      <div class="dark-context">
+        <div class="card card-dark dark-normal-shadow">
+          <h4>Normal gray shadow on dark card</h4>
+          <p>Gray/black shadow is not a glow.</p>
+        </div>
+        <div class="card card-dark dark-focus-ring">
+          <h4>Focus ring (spread, no blur)</h4>
+          <p>Functional ring, not decorative glow.</p>
+        </div>
+        <div class="card card-dark dark-subtle-shadow">
+          <h4>Tiny blur (&lt;5px)</h4>
+          <p>Too subtle to be "glowing".</p>
+        </div>
+        <div class="card card-dark dark-elevated-sm">
+          <h4>Small dark elevation</h4>
+          <p>Subtle shadow on dark card.</p>
+        </div>
+        <div class="card card-dark dark-elevated-md">
+          <h4>Medium dark elevation</h4>
+          <p>Standard dark card shadow.</p>
+        </div>
+        <div class="card card-dark dark-elevated-lg">
+          <h4>Large dark elevation</h4>
+          <p>Prominent shadow on dark card.</p>
+        </div>
+        <div class="card card-dark dark-elevated-inset">
+          <h4>Inset shadow</h4>
+          <p>Inner shadow, not a glow.</p>
+        </div>
+        <div class="card card-dark dark-elevated-multi">
+          <h4>Multi-shadow dark card</h4>
+          <p>Layered gray shadows, no color glow.</p>
+        </div>
+      </div>
+
+      <h3>Medium-gray background (not dark enough)</h3>
+      <div class="card card-medium medium-gray-shadow">
+        <h4>Not dark enough background</h4>
+        <p>Medium gray doesn't qualify as "dark mode".</p>
+      </div>
+
+      <h3>Light elevated cards</h3>
+      <div class="card card-light elevated-sm-light">
+        <h4>Small elevation (Tailwind shadow-sm)</h4>
+        <p>Standard subtle card shadow.</p>
+      </div>
+      <div class="card card-light elevated-md-light">
+        <h4>Medium elevation (Tailwind shadow-md)</h4>
+        <p>Standard card shadow.</p>
+      </div>
+      <div class="card card-light elevated-warm-light">
+        <h4>Warm deep shadow</h4>
+        <p>Large spread neutral shadow.</p>
+      </div>
+    </div>
+  </div>
+  <script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/icon-tile-stack.html 🔗

@@ -0,0 +1,247 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Icon-Tile-Stack — Should Flag vs Should Pass</title>
+  <style>
+    /* ─── Layout convention: two-column fixture ─────────────────────────
+       Left column = patterns the rule SHOULD flag.
+       Right column = legitimate patterns the rule should NOT flag.
+       Each test case is annotated with a unique <h3> text so the test
+       can match snippets back to expectations.
+       ──────────────────────────────────────────────────────────────── */
+    body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }
+    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 960px; margin: 0 auto; }
+    .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
+    .case { margin-bottom: 24px; padding: 16px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }
+    .case h3 { font-size: 18px; margin: 0 0 4px; color: #0f172a; }
+    .case p { font-size: 14px; margin: 0; color: #64748b; }
+
+    /* ── FLAG cases ── */
+
+    /* 1: pastel-gradient icon-tile (the canonical massive-icons.html shape) */
+    .icon-pastel {
+      width: 80px; height: 80px;
+      border-radius: 20px;
+      background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
+      display: flex; align-items: center; justify-content: center;
+      margin-bottom: 16px;
+    }
+
+    /* 2: solid-color icon-tile, white svg inside (no gradient) */
+    .icon-solid {
+      width: 64px; height: 64px;
+      border-radius: 16px;
+      background: #3b82f6;
+      display: flex; align-items: center; justify-content: center;
+      margin-bottom: 16px;
+    }
+
+    /* 3: white-bg outlined icon-tile (no chromatic background at all) */
+    .icon-white {
+      width: 72px; height: 72px;
+      border-radius: 18px;
+      background: #ffffff;
+      border: 1px solid #e2e8f0;
+      display: flex; align-items: center; justify-content: center;
+      margin-bottom: 16px;
+    }
+
+    /* 4: larger square (still inside the 32–128 range) */
+    .icon-large {
+      width: 96px; height: 96px;
+      border-radius: 24px;
+      background: #f1f5f9;
+      display: flex; align-items: center; justify-content: center;
+      margin-bottom: 16px;
+    }
+
+    /* 5: inline emoji icon — tile contains an emoji/symbol character directly,
+       not an <svg> or <i> child. This is the "card-icon" pattern from many
+       generated demos. */
+    .icon-emoji {
+      width: 48px; height: 48px;
+      border-radius: 12px;
+      background: linear-gradient(135deg, #8b5cf6, #a855f7);
+      display: flex; align-items: center; justify-content: center;
+      font-size: 24px;
+      margin-bottom: 16px;
+    }
+
+    /* ── PASS cases ── */
+
+    /* a: round avatar above name (border-radius: 50% — circle, not rounded square) */
+    .avatar-round {
+      width: 64px; height: 64px;
+      border-radius: 50%;
+      background: #f59e0b;
+      display: flex; align-items: center; justify-content: center;
+      margin-bottom: 16px;
+    }
+
+    /* b: wide thumbnail above article title (16:9, much wider than tall) */
+    .thumb-wide {
+      width: 200px; height: 112px;
+      border-radius: 8px;
+      background: #cbd5e1;
+      margin-bottom: 16px;
+    }
+
+    /* c: inline icon next to heading (icon and h3 on the same row, NOT stacked) */
+    .inline-row { display: flex; align-items: center; gap: 12px; }
+    .inline-row .icon-inline {
+      width: 80px; height: 80px;
+      border-radius: 20px;
+      background: #ddd6fe;
+      display: flex; align-items: center; justify-content: center;
+      flex-shrink: 0;
+    }
+
+    /* d: nothing above the heading at all */
+    .no-icon h3 { margin-top: 0; }
+
+    /* e: icon-tile that's too small (under 32px) — out of size range */
+    .icon-tiny {
+      width: 24px; height: 24px;
+      border-radius: 6px;
+      background: #93c5fd;
+      display: flex; align-items: center; justify-content: center;
+      margin-bottom: 16px;
+    }
+
+    /* f: oversized image card / hero image (over 128px) — out of size range */
+    .img-huge {
+      width: 200px; height: 200px;
+      border-radius: 16px;
+      background: #cbd5e1;
+      margin-bottom: 16px;
+    }
+  </style>
+</head>
+<body>
+  <div class="grid">
+
+    <!-- ════════════════════════════════════════════════════════════════
+         LEFT COLUMN: should flag
+         ═══════════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="flag">
+      <h2>Should flag</h2>
+
+      <div class="case">
+        <div class="icon-pastel">
+          <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="1.5">
+            <polyline points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
+          </svg>
+        </div>
+        <h3>Lightning Fast</h3>
+        <p>Built for speed and performance from the ground up.</p>
+      </div>
+
+      <div class="case">
+        <div class="icon-solid">
+          <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.5">
+            <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
+          </svg>
+        </div>
+        <h3>Secure Storage</h3>
+        <p>Enterprise-grade security for your peace of mind.</p>
+      </div>
+
+      <div class="case">
+        <div class="icon-white">
+          <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#0f172a" stroke-width="1.5">
+            <circle cx="12" cy="12" r="10"/>
+            <path d="M12 6v6l4 2"/>
+          </svg>
+        </div>
+        <h3>Easy Setup</h3>
+        <p>Get started in minutes with zero configuration.</p>
+      </div>
+
+      <div class="case">
+        <div class="icon-large">
+          <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#0f172a" stroke-width="1.5">
+            <line x1="18" y1="20" x2="18" y2="10"/>
+            <line x1="12" y1="20" x2="12" y2="4"/>
+            <line x1="6" y1="20" x2="6" y2="14"/>
+          </svg>
+        </div>
+        <h3>Powerful Analytics</h3>
+        <p>Insights that drive smarter business decisions.</p>
+      </div>
+
+      <div class="case">
+        <div class="icon-emoji">&#9889;</div>
+        <h3>Emoji Inline Icon</h3>
+        <p>The tile contains an emoji directly, no SVG child.</p>
+      </div>
+    </div>
+
+    <!-- ════════════════════════════════════════════════════════════════
+         RIGHT COLUMN: should pass
+         ═══════════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="pass">
+      <h2>Should pass</h2>
+
+      <!-- a: round avatar (circle, not rounded square) -->
+      <div class="case">
+        <div class="avatar-round">
+          <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
+            <circle cx="12" cy="8" r="4"/>
+            <path d="M4 22v-2a8 8 0 0116 0v2"/>
+          </svg>
+        </div>
+        <h3>Sarah Chen</h3>
+        <p>Product designer based in Berlin.</p>
+      </div>
+
+      <!-- b: wide 16:9 thumbnail (aspect ratio out of range) -->
+      <div class="case">
+        <div class="thumb-wide"></div>
+        <h3>Article Headline</h3>
+        <p>A summary of an interesting article.</p>
+      </div>
+
+      <!-- c: side-by-side icon and heading (not stacked vertically) -->
+      <div class="case">
+        <div class="inline-row">
+          <div class="icon-inline">
+            <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="1.5">
+              <circle cx="12" cy="12" r="10"/>
+            </svg>
+          </div>
+          <div>
+            <h3>Inline Side By Side</h3>
+            <p>Icon sits next to the heading, not above it.</p>
+          </div>
+        </div>
+      </div>
+
+      <!-- d: no icon above the heading at all -->
+      <div class="case no-icon">
+        <h3>Plain Heading No Icon</h3>
+        <p>Just a heading with body text, nothing above.</p>
+      </div>
+
+      <!-- e: icon-tile too small (24px — under 32px floor) -->
+      <div class="case">
+        <div class="icon-tiny">
+          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#1e40af" stroke-width="2">
+            <circle cx="12" cy="12" r="10"/>
+          </svg>
+        </div>
+        <h3>Tiny Icon Above Me</h3>
+        <p>The icon container is smaller than 32px.</p>
+      </div>
+
+      <!-- f: huge image / hero (200px — over 128px ceiling) -->
+      <div class="case">
+        <div class="img-huge"></div>
+        <h3>Huge Hero Image</h3>
+        <p>The container is larger than the size cap.</p>
+      </div>
+    </div>
+  </div>
+  <script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/jsx-should-flag.jsx 🔗

@@ -0,0 +1,56 @@
+// A typical Next.js/React component with anti-patterns
+
+import React from 'react';
+import { motion } from 'framer-motion';
+
+export function FeatureCard({ title, description, icon }) {
+  return (
+    <div className="border-l-4 border-blue-500 rounded-lg bg-white p-6 shadow-md">
+      <div className="flex items-center gap-4">
+        <div className="text-purple-500 text-3xl">{icon}</div>
+        <h3 className="text-xl font-bold">{title}</h3>
+      </div>
+      <p className="text-gray-400 mt-2">{description}</p>
+    </div>
+  );
+}
+
+export function HeroSection() {
+  return (
+    <section className="bg-black py-20 text-center">
+      <h1
+        className="text-5xl font-bold bg-gradient-to-r from-purple-400 to-cyan-400 bg-clip-text text-transparent"
+      >
+        Welcome to the Future
+      </h1>
+      <p className="mt-4 text-gray-400">Build something amazing today</p>
+    </section>
+  );
+}
+
+export function StatsCard({ value, label }) {
+  return (
+    <div
+      style={{
+        borderLeft: '4px solid #3b82f6',
+        borderRadius: '12px',
+        padding: '16px',
+        background: '#fff',
+      }}
+    >
+      <span style={{ fontSize: '32px', fontFamily: "'Inter', sans-serif" }}>{value}</span>
+      <p>{label}</p>
+    </div>
+  );
+}
+
+export function AnimatedPanel({ children }) {
+  return (
+    <motion.div
+      className="animate-bounce"
+      style={{ transition: 'width 0.3s ease' }}
+    >
+      {children}
+    </motion.div>
+  );
+}

tests/fixtures/antipatterns/jsx-should-pass.jsx 🔗

@@ -0,0 +1,33 @@
+// Clean React component -- no anti-patterns
+
+import React from 'react';
+
+export function FeatureCard({ title, description, icon }) {
+  return (
+    <div className="rounded-lg bg-white p-6 shadow-sm ring-1 ring-gray-200">
+      <div className="flex items-center gap-4">
+        <div className="text-teal-600 text-2xl">{icon}</div>
+        <h3 className="text-lg font-semibold text-gray-900">{title}</h3>
+      </div>
+      <p className="text-gray-600 mt-2">{description}</p>
+    </div>
+  );
+}
+
+export function HeroSection() {
+  return (
+    <section className="bg-gray-950 py-20">
+      <h1 className="text-5xl font-bold text-white">Welcome</h1>
+      <p className="mt-4 text-gray-300">Build something amazing today</p>
+    </section>
+  );
+}
+
+export function StatsCard({ value, label }) {
+  return (
+    <div className="rounded-lg p-6 ring-1 ring-gray-200">
+      <span className="text-3xl font-bold tabular-nums">{value}</span>
+      <p className="text-gray-600 mt-1">{label}</p>
+    </div>
+  );
+}

tests/fixtures/antipatterns/layout.html 🔗

@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Layout Anti-Patterns — Should Flag vs Should Pass</title>
+  <script src="https://cdn.tailwindcss.com"></script>
+  <style>
+    body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; color: #111827; }
+    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }
+    .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
+    .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }
+  </style>
+</head>
+<body>
+  <div class="grid">
+
+    <!-- ════════════════════════════════════════════════════════════
+         LEFT COLUMN: should flag
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="flag">
+      <h2>Should flag</h2>
+
+      <h3>Nested cards (cardocalypse)</h3>
+
+      <!-- Classic: card inside card -->
+      <div class="bg-white rounded-lg shadow-md p-6 mb-4">
+        <h4 class="text-lg font-semibold mb-3">Outer Card</h4>
+        <div class="bg-white rounded-lg shadow-md p-4">
+          <h5 class="font-medium">Inner Card</h5>
+          <p class="text-sm text-gray-600">Card inside card.</p>
+        </div>
+      </div>
+
+      <!-- Three levels deep -->
+      <div class="bg-white rounded-xl shadow-lg p-6 mb-4">
+        <h4 class="text-lg font-semibold mb-3">Level 1</h4>
+        <div class="bg-gray-50 rounded-lg shadow-sm p-4 mb-3">
+          <h5 class="font-medium mb-2">Level 2</h5>
+          <div class="bg-white rounded-md shadow-sm p-3">
+            <p class="text-sm">Level 3 — nesting inception.</p>
+          </div>
+        </div>
+      </div>
+
+      <!-- CSS variant: border + bg + shadow nesting -->
+      <div style="background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 1.5rem; margin-bottom: 1rem;">
+        <h4 style="font-weight: 600; margin-bottom: 0.75rem;">Outer (CSS)</h4>
+        <div style="background: #f9fafb; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); padding: 1rem;">
+          <p style="font-size: 0.875rem; color: #6b7280;">Inner card via CSS.</p>
+        </div>
+      </div>
+
+      <!-- shadcn-style Card nested in Card -->
+      <div class="rounded-lg border bg-white shadow-sm p-6 mb-4" data-component="card">
+        <h4 class="font-semibold mb-3">shadcn-style Outer Card</h4>
+        <div class="rounded-lg border bg-white shadow-sm p-4" data-component="card">
+          <p class="text-sm text-gray-600">Another shadcn Card nested inside.</p>
+        </div>
+      </div>
+    </div>
+
+    <!-- ════════════════════════════════════════════════════════════
+         RIGHT COLUMN: should pass
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="pass">
+      <h2>Should pass</h2>
+
+      <h3>shadcn Card sub-components (not nested cards)</h3>
+      <div class="rounded-lg border bg-white shadow-sm mb-4">
+        <div class="flex flex-col space-y-1.5 p-6">
+          <h4 class="text-lg font-semibold">Card Title</h4>
+          <p class="text-sm text-gray-500">Card description goes here.</p>
+        </div>
+        <div class="p-6 pt-0">
+          <p class="text-sm text-gray-600">CardContent — a sub-section, not a nested card. No shadow, no border-radius of its own.</p>
+        </div>
+        <div class="flex items-center p-6 pt-0">
+          <button class="px-4 py-2 bg-gray-900 text-white text-sm rounded-md">Action</button>
+        </div>
+      </div>
+
+      <h3>Card with form inputs</h3>
+      <div class="bg-white rounded-lg shadow-sm p-6 mb-4 border">
+        <h4 class="font-semibold mb-4">Settings</h4>
+        <div class="space-y-3">
+          <input type="text" placeholder="Name" class="w-full px-3 py-2 border rounded-md shadow-sm text-sm">
+          <input type="email" placeholder="Email" class="w-full px-3 py-2 border rounded-md shadow-sm text-sm">
+          <select class="w-full px-3 py-2 border rounded-md shadow-sm text-sm bg-white">
+            <option>Select option</option>
+          </select>
+          <textarea placeholder="Bio" class="w-full px-3 py-2 border rounded-md shadow-sm text-sm h-20 resize-none"></textarea>
+        </div>
+      </div>
+
+      <h3>Card with code block</h3>
+      <div class="bg-white rounded-lg shadow-sm p-6 mb-4 border">
+        <h4 class="font-semibold mb-3">Installation</h4>
+        <pre class="bg-gray-900 text-gray-100 rounded-md p-4 text-sm overflow-x-auto"><code>npm install @acme/sdk</code></pre>
+      </div>
+
+      <h3>Card with badges</h3>
+      <div class="bg-white rounded-lg shadow-sm p-6 mb-4 border">
+        <h4 class="font-semibold mb-3">Tags</h4>
+        <div class="flex flex-wrap gap-2">
+          <span class="px-2 py-1 bg-blue-100 text-blue-700 rounded-full text-xs font-medium">React</span>
+          <span class="px-2 py-1 bg-green-100 text-green-700 rounded-full text-xs font-medium">TypeScript</span>
+          <span class="px-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs font-medium">Tailwind</span>
+        </div>
+      </div>
+
+      <h3>Card with image</h3>
+      <div class="bg-white rounded-lg shadow-sm p-6 mb-4 border">
+        <div class="w-full h-32 bg-gradient-to-br from-amber-400 to-rose-500 rounded-lg shadow-inner mb-4"></div>
+        <h4 class="font-semibold">Project Preview</h4>
+        <p class="text-sm text-gray-600 mt-1">Image with rounded corners and shadow.</p>
+      </div>
+
+      <h3>Pricing cards (intentionally similar)</h3>
+      <div class="grid grid-cols-3 gap-2 mb-4">
+        <div class="bg-white rounded-lg shadow-sm p-4 border">
+          <h4 class="font-semibold mb-1 text-sm">Free</h4>
+          <div class="text-lg font-bold mb-2">$0</div>
+        </div>
+        <div class="bg-white rounded-lg shadow-sm p-4 border-2 border-blue-500">
+          <h4 class="font-semibold mb-1 text-sm">Pro</h4>
+          <div class="text-lg font-bold mb-2">$29</div>
+        </div>
+        <div class="bg-white rounded-lg shadow-sm p-4 border">
+          <h4 class="font-semibold mb-1 text-sm">Enterprise</h4>
+          <div class="text-lg font-bold mb-2">Custom</div>
+        </div>
+      </div>
+    </div>
+  </div>
+  <script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/legitimate-borders.html 🔗

@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Legitimate Border Patterns — Should NOT Flag</title>
+  <style>
+    body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; color: #111827; }
+    h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }
+    h2 { font-size: 1.125rem; margin: 2rem 0 0.75rem; color: #6b7280; }
+    .demo { max-width: 36rem; margin-bottom: 1.5rem; }
+  </style>
+</head>
+<body>
+  <h1>Legitimate Border Patterns</h1>
+  <p style="color: #6b7280; margin-bottom: 2rem;">Every border here is a well-established web pattern. None should be flagged.</p>
+
+  <!-- 1. BLOCKQUOTE — the classic border-left usage -->
+  <h2>Blockquotes</h2>
+  <div class="demo">
+    <blockquote style="border-left: 4px solid #d1d5db; padding-left: 1rem; margin: 0; color: #4b5563; font-style: italic;">
+      "Design is not just what it looks like and feels like. Design is how it works."
+    </blockquote>
+  </div>
+
+  <!-- 2. SIDEBAR NAV — active state indicator -->
+  <h2>Sidebar Navigation (Active State)</h2>
+  <div class="demo">
+    <nav style="width: 200px; background: white; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
+      <a href="#" style="display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;">Dashboard</a>
+      <a href="#" style="display: block; padding: 0.75rem 1rem; color: #1d4ed8; text-decoration: none; border-left: 3px solid #3b82f6; background: #eff6ff; font-weight: 500;">Projects</a>
+      <a href="#" style="display: block; padding: 0.75rem 1rem; color: #6b7280; text-decoration: none; border-left: 3px solid transparent;">Settings</a>
+    </nav>
+  </div>
+
+  <!-- 3. FORM VALIDATION — error indicator on input -->
+  <h2>Form Validation</h2>
+  <div class="demo">
+    <div style="margin-bottom: 1rem;">
+      <label style="display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 0.25rem;">Email</label>
+      <input type="email" value="not-an-email" style="width: 100%; padding: 0.5rem 0.75rem; border: 1px solid #fca5a5; border-left: 3px solid #ef4444; border-radius: 6px; outline: none; font-size: 0.875rem;">
+      <p style="color: #ef4444; font-size: 0.75rem; margin-top: 0.25rem;">Please enter a valid email address.</p>
+    </div>
+  </div>
+
+  <!-- 4. TIMELINE / STEPPER -->
+  <h2>Timeline</h2>
+  <div class="demo">
+    <div style="border-left: 3px solid #e5e7eb; padding-left: 1.5rem; margin-left: 0.75rem;">
+      <div style="position: relative; padding-bottom: 1.5rem;">
+        <div style="position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;"></div>
+        <p style="font-weight: 500; margin: 0;">Order placed</p>
+        <p style="font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;">March 15, 2026</p>
+      </div>
+      <div style="position: relative; padding-bottom: 1.5rem;">
+        <div style="position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #3b82f6; border: 2px solid white;"></div>
+        <p style="font-weight: 500; margin: 0;">Shipped</p>
+        <p style="font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;">March 16, 2026</p>
+      </div>
+      <div style="position: relative;">
+        <div style="position: absolute; left: -2.05rem; top: 0.125rem; width: 10px; height: 10px; border-radius: 50%; background: #d1d5db; border: 2px solid white;"></div>
+        <p style="font-weight: 500; margin: 0; color: #9ca3af;">Delivered</p>
+        <p style="font-size: 0.75rem; color: #6b7280; margin: 0.125rem 0 0;">Expected March 18</p>
+      </div>
+    </div>
+  </div>
+
+  <!-- 5. CODE BLOCK — diff highlighting -->
+  <h2>Code Diff Highlighting</h2>
+  <div class="demo">
+    <pre style="background: #1e293b; color: #e2e8f0; padding: 1rem; border-radius: 8px; font-size: 0.8125rem; overflow-x: auto; margin: 0;">
+<code><span style="display: block; border-left: 3px solid #ef4444; padding-left: 0.75rem; background: rgba(239,68,68,0.1);">- const old = getValue();</span>
+<span style="display: block; border-left: 3px solid #22c55e; padding-left: 0.75rem; background: rgba(34,197,94,0.1);">+ const value = getNewValue();</span>
+<span style="display: block; padding-left: calc(3px + 0.75rem);">  return value;</span></code></pre>
+  </div>
+
+  <!-- 6. TAB ACTIVE STATE — bottom border -->
+  <h2>Tab Navigation</h2>
+  <div class="demo">
+    <div style="display: flex; border-bottom: 1px solid #e5e7eb;">
+      <button style="padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #1d4ed8; border-bottom: 2px solid #3b82f6; font-weight: 500; cursor: pointer;">Overview</button>
+      <button style="padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;">Analytics</button>
+      <button style="padding: 0.75rem 1.25rem; border: none; background: none; font-size: 0.875rem; color: #6b7280; border-bottom: 2px solid transparent; cursor: pointer;">Settings</button>
+    </div>
+  </div>
+
+  <!-- 7. TABLE CELL BORDERS -->
+  <h2>Data Table</h2>
+  <div class="demo">
+    <table style="width: 100%; border-collapse: collapse; font-size: 0.875rem;">
+      <thead>
+        <tr>
+          <th style="text-align: left; padding: 0.5rem; border-bottom: 2px solid #111827;">Name</th>
+          <th style="text-align: right; padding: 0.5rem; border-bottom: 2px solid #111827;">Revenue</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr><td style="padding: 0.5rem; border-bottom: 1px solid #e5e7eb;">Acme Corp</td><td style="text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;">$1.2M</td></tr>
+        <tr><td style="padding: 0.5rem; border-bottom: 1px solid #e5e7eb;">Globex</td><td style="text-align: right; padding: 0.5rem; border-bottom: 1px solid #e5e7eb;">$850K</td></tr>
+      </tbody>
+    </table>
+  </div>
+
+  <!-- 8. ALERT BANNER — full-width, not a card -->
+  <h2>Alert Banner</h2>
+  <div class="demo">
+    <div style="border-left: 4px solid #f59e0b; background: #fffbeb; padding: 0.75rem 1rem; font-size: 0.875rem; color: #92400e;">
+      <strong>Warning:</strong> Your trial expires in 3 days. <a href="#" style="color: #d97706;">Upgrade now</a>
+    </div>
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/linked-stylesheet.html 🔗

@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Patterns From Linked Stylesheet</title>
+  <link rel="stylesheet" href="external-styles.css">
+  <style>
+    body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }
+    h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }
+    h2 { font-size: 1.125rem; margin: 2rem 0 0.75rem; color: #6b7280; }
+    p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }
+    .cards { display: grid; gap: 1rem; max-width: 28rem; }
+  </style>
+</head>
+<body>
+  <h1>Linked Stylesheet Anti-Patterns</h1>
+  <p class="intro">
+    These elements get their anti-pattern styles from an external CSS file.
+    Regex-only scanning misses these — only computed style analysis catches them.
+  </p>
+
+  <h2>Side-Tab (from external CSS)</h2>
+  <div class="cards">
+    <div class="external-side-tab">
+      <h3 style="font-weight: 600;">External side-tab class</h3>
+      <p style="font-size: 0.875rem; color: #6b7280;">border-left + border-radius from linked stylesheet.</p>
+    </div>
+  </div>
+
+  <h2>Top Accent + Rounded (from external CSS)</h2>
+  <div class="cards">
+    <div class="external-top-accent">
+      <h3 style="font-weight: 600;">External top accent class</h3>
+      <p style="font-size: 0.875rem; color: #6b7280;">border-top + border-radius from linked stylesheet.</p>
+    </div>
+  </div>
+
+  <h2>Clean Card (from external CSS)</h2>
+  <div class="cards">
+    <div class="external-clean">
+      <h3 style="font-weight: 600;">External clean card</h3>
+      <p style="font-size: 0.875rem; color: #6b7280;">Uniform 1px border — should NOT flag.</p>
+    </div>
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/modern-color-borders.html 🔗

@@ -0,0 +1,326 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Side-Tab with Modern Color Formats (oklch/lab/lch/hsl/hwb)</title>
+  <style>
+    /* Two-column fixture: left col = should-flag, right col = should-pass.
+       The side-tab snippet doesn't embed identifying text, so the test
+       asserts finding counts against the structural ids (#flag-N / #pass-N)
+       by reading the element via data attributes in the check path. Since
+       that isn't available to the Node fixture runner, we instead place a
+       unique number of FLAG elements (each a distinct side-tab) and verify
+       the side-tab count. */
+
+    /* CSS custom properties — variables used by the var()-case fixtures.
+       jsdom's CSSOM drops any border shorthand containing var(), which used
+       to make every side-tab built with CSS variables invisible to the
+       detector. These cases regression-test the fallback pass that parses
+       border shorthands directly off the rule (not the computed style). */
+    :root {
+      --brand: #87a8ff;
+      --warn: #8a6a2f;
+      --terracotta: #aa674d;
+      --line: #e5e7eb;    /* neutral gray — must NOT trigger */
+      --rule: #f3f4f6;    /* near-white — must NOT trigger */
+    }
+
+    body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; background: #fafafa; }
+    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1120px; margin: 0 auto; }
+    .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
+    .case { padding: 12px 16px; margin-bottom: 16px; }
+    .case h3 { font-size: 14px; margin: 0 0 4px; }
+    .case p { font-size: 13px; margin: 0; color: #64748b; }
+
+    /* ── FLAG cases: colored side-tab borders using modern color formats ── */
+
+    /* 1: oklch border-left + border-radius (the canonical real-page bug) */
+    #flag-oklch-1 {
+      width: 400px;
+      background: oklch(100% 0 0);
+      border-radius: 4px;
+      border-left: 3px solid oklch(65% 0.12 250);
+    }
+
+    /* 2: oklch mixed — neutral 1px border all around + colored 4px left +
+          border-radius (mirrors .medication-card from the reproducer) */
+    #flag-oklch-2 {
+      width: 400px;
+      background: oklch(100% 0 0);
+      border: 1px solid oklch(80% 0.05 250);
+      border-left: 4px solid oklch(75% 0.15 45);
+      border-radius: 4px;
+    }
+
+    /* 3: oklch alert pattern — colored 4px border-left + border-radius */
+    #flag-oklch-3 {
+      width: 400px;
+      background: oklch(98% 0.01 60);
+      border-left: 4px solid oklch(75% 0.18 45);
+      border-radius: 4px;
+    }
+
+    /* 4: oklab border-left + border-radius */
+    #flag-oklab-1 {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 6px;
+      border-left: 4px solid oklab(60% 0.1 0.15);
+    }
+
+    /* 5: lch border-left + border-radius */
+    #flag-lch-1 {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 6px;
+      border-left: 4px solid lch(55% 60 250);
+    }
+
+    /* 6: lab border-left + border-radius */
+    #flag-lab-1 {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 6px;
+      border-left: 4px solid lab(50% 40 -30);
+    }
+
+    /* 7: card-shaped <label> with colored oklch side border + radius
+          (the checklist-item pattern from preop-portal reproducer) */
+    label#flag-label-oklch {
+      display: grid;
+      grid-template-columns: auto 1fr;
+      width: 400px;
+      padding: 16px;
+      background: oklch(100% 0 0);
+      border-radius: 4px;
+      border-left: 3px solid oklch(65% 0.12 250);
+      cursor: pointer;
+    }
+
+    /* 8: card-shaped <label> with colored rgb side border + radius —
+          proves the fix is not oklch-specific */
+    label#flag-label-rgb {
+      display: block;
+      width: 400px;
+      padding: 16px;
+      background: #ffffff;
+      border-radius: 6px;
+      border-left: 4px solid #3b82f6;
+      cursor: pointer;
+    }
+
+    /* 9: CSS variable in shorthand — the canonical real-page pattern from
+          evals (e.g. 09-physics-study: border-left:8px solid var(--brand-2)).
+          jsdom silently drops this entire declaration, so the computed-style
+          path used to miss it. */
+    #flag-var-shorthand {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 5px solid var(--brand);
+    }
+
+    /* 10: CSS variable + neutral 1px all-around border — mirrors the
+           medication-card pattern with a colored accent on the left. */
+    #flag-var-mixed {
+      width: 400px;
+      background: #ffffff;
+      border: 1px solid var(--line);
+      border-left: 4px solid var(--warn);
+      border-radius: 4px;
+    }
+
+    /* 11: CSS variable shorthand, right side. Ensures the fallback works
+           for both inline axes, not just border-left. */
+    #flag-var-right {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-right: 4px solid var(--terracotta);
+    }
+
+    /* 12: card-shaped <label> with var() side border + radius —
+           the checklist-row shape, built with tokens. */
+    label#flag-label-var {
+      display: block;
+      width: 400px;
+      padding: 16px;
+      background: #ffffff;
+      border-radius: 6px;
+      border-left: 4px solid var(--brand);
+      cursor: pointer;
+    }
+
+    /* ── PASS cases: legitimate or neutral borders, must NOT fire ── */
+
+    /* 1: oklch chroma ~0 — true neutral gray border should not fire */
+    #pass-oklch-neutral {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 3px solid oklch(80% 0 0);
+    }
+
+    /* 2: oklch low chroma (0.01) — still perceptually neutral */
+    #pass-oklch-nearneutral {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 3px solid oklch(75% 0.01 250);
+    }
+
+    /* 3: oklch colored border all around (not one-sided) — no side-tab */
+    #pass-oklch-allsides {
+      width: 400px;
+      background: #ffffff;
+      border: 3px solid oklch(65% 0.12 250);
+      border-radius: 4px;
+    }
+
+    /* 4: oklch colored 1px border-left (too thin to qualify) */
+    #pass-oklch-thin {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 1px solid oklch(65% 0.12 250);
+    }
+
+    /* 5: lab neutral (chroma near zero) — must not fire */
+    #pass-lab-neutral {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 3px solid lab(50% 0 0);
+    }
+
+    /* 6: lch neutral (chroma 0) */
+    #pass-lch-neutral {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 3px solid lch(50% 0 0);
+    }
+
+    /* 7: plain inline form label — no border, no padding, no radius.
+          Must NOT be flagged for anything. */
+    label#pass-label-plain {
+      font-size: 14px;
+      font-weight: 500;
+      color: #374151;
+    }
+
+    /* 8: form label with a small neutral gray left border — neutral color
+          should not fire the colored side-tab rule. */
+    label#pass-label-neutral-border {
+      display: block;
+      width: 400px;
+      padding: 8px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 3px solid #e5e7eb;
+    }
+
+    /* 9: inline label inside a form row — default inline layout,
+          no borders, must not fire. */
+    label#pass-label-inline {
+      font-size: 13px;
+      color: #6b7280;
+      margin-right: 8px;
+    }
+
+    /* 10: label with 1px colored left border — too thin to qualify
+          (matches the existing 1px thin rule for divs). */
+    label#pass-label-thin {
+      display: block;
+      width: 400px;
+      padding: 8px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 1px solid #3b82f6;
+    }
+
+    /* 11: CSS variable resolving to a neutral gray — must NOT fire,
+           even though the border shorthand contains var(). */
+    #pass-var-neutral {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 3px solid var(--line);
+    }
+
+    /* 12: thin (1px) var-based left border — under the side-tab width
+           threshold even when resolved. */
+    #pass-var-thin {
+      width: 400px;
+      background: #ffffff;
+      border-radius: 4px;
+      border-left: 1px solid var(--brand);
+    }
+
+    /* 13: var-based uniform border on all four sides. Not a side-tab. */
+    #pass-var-allsides {
+      width: 400px;
+      background: #ffffff;
+      border: 3px solid var(--brand);
+      border-radius: 4px;
+    }
+  </style>
+</head>
+<body>
+  <div class="grid">
+    <div class="col" data-col="flag">
+      <h2>Should flag</h2>
+      <div class="case" id="flag-oklch-1"><h3>oklch checklist</h3><p>oklch border-left 3px + radius</p></div>
+      <div class="case" id="flag-oklch-2"><h3>oklch medication card</h3><p>neutral all + colored left 4px</p></div>
+      <div class="case" id="flag-oklch-3"><h3>oklch alert</h3><p>warm border-left 4px + radius</p></div>
+      <div class="case" id="flag-oklab-1"><h3>oklab card</h3><p>oklab border-left 4px + radius</p></div>
+      <div class="case" id="flag-lch-1"><h3>lch card</h3><p>lch border-left 4px + radius</p></div>
+      <div class="case" id="flag-lab-1"><h3>lab card</h3><p>lab border-left 4px + radius</p></div>
+      <label class="case" id="flag-label-oklch">
+        <input type="checkbox">
+        <span>Card-shaped label with oklch side border</span>
+      </label>
+      <label class="case" id="flag-label-rgb">
+        <input type="checkbox">
+        <span>Card-shaped label with rgb side border</span>
+      </label>
+      <div class="case" id="flag-var-shorthand"><h3>var() shorthand</h3><p>border-left 5px + var(--brand)</p></div>
+      <div class="case" id="flag-var-mixed"><h3>var() mixed</h3><p>neutral all + colored var left</p></div>
+      <div class="case" id="flag-var-right"><h3>var() right side</h3><p>border-right 4px + var(--terracotta)</p></div>
+      <label class="case" id="flag-label-var">
+        <input type="checkbox">
+        <span>Card-shaped label with var() side border</span>
+      </label>
+    </div>
+    <div class="col" data-col="pass">
+      <h2>Should pass</h2>
+      <div class="case" id="pass-oklch-neutral"><h3>oklch neutral</h3><p>chroma 0 gray</p></div>
+      <div class="case" id="pass-oklch-nearneutral"><h3>oklch near-neutral</h3><p>chroma 0.01</p></div>
+      <div class="case" id="pass-oklch-allsides"><h3>oklch all sides</h3><p>uniform border</p></div>
+      <div class="case" id="pass-oklch-thin"><h3>oklch thin</h3><p>1px too thin</p></div>
+      <div class="case" id="pass-lab-neutral"><h3>lab neutral</h3><p>chroma 0</p></div>
+      <div class="case" id="pass-lch-neutral"><h3>lch neutral</h3><p>chroma 0</p></div>
+      <div>
+        <label for="pass-email" id="pass-label-plain">Email address</label>
+        <input type="email" id="pass-email">
+      </div>
+      <label id="pass-label-neutral-border">
+        <input type="checkbox">
+        <span>Label with neutral gray left border</span>
+      </label>
+      <form>
+        <label for="pass-name" id="pass-label-inline">Name:</label>
+        <input type="text" id="pass-name">
+      </form>
+      <label id="pass-label-thin">
+        <input type="checkbox">
+        <span>Label with 1px colored left border (too thin)</span>
+      </label>
+      <div class="case" id="pass-var-neutral"><h3>var() neutral</h3><p>--line resolves to gray</p></div>
+      <div class="case" id="pass-var-thin"><h3>var() thin</h3><p>1px too thin to qualify</p></div>
+      <div class="case" id="pass-var-allsides"><h3>var() all sides</h3><p>uniform, not a side-tab</p></div>
+    </div>
+  </div>
+  <script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/motion.html 🔗

@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Motion Anti-Patterns — Should Flag vs Should Pass</title>
+  <style>
+    /* ─── Two-column fixture convention ─────────────────────────────
+       Left column = should flag. Right column = should pass.
+       Tests assert (a) every flag-column rule fires and (b) the total
+       finding count matches the number of flag cases (no false
+       positives leaking from the pass column).
+       ──────────────────────────────────────────────────────────── */
+    body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 24px; margin: 0; }
+    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }
+    .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
+    .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }
+    .demo { padding: 14px 16px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); margin-bottom: 10px; }
+    .demo h4 { font-weight: 600; font-size: 13px; margin: 0; }
+    .demo p { font-size: 12px; color: #6b7280; margin: 4px 0 0; }
+
+    /* ── FLAG: bounce / elastic easing ── */
+    @keyframes bounce-keyframe {
+      0%, 100% { transform: translateY(0); }
+      50% { transform: translateY(-10px); }
+    }
+    .bounce-animation { animation: bounce-keyframe 1s infinite; }
+    .elastic-transition { transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); }
+
+    /* ── FLAG: layout property transitions ── */
+    .width-transition { transition: width 0.3s ease; }
+    .height-transition { transition: height 0.4s ease-out; }
+    .padding-transition { transition: padding 0.2s linear; }
+    .margin-transition { transition: margin 0.3s ease-in; }
+    .max-height-transition { transition: max-height 0.5s ease; }
+    .multi-layout-transition { transition: width 0.3s ease, height 0.3s ease; }
+    .mixed-transition { transition: width 0.3s ease, opacity 0.3s ease; }
+    .transition-property-width { transition-property: width; transition-duration: 0.3s; }
+
+    /* ── PASS: good easing ── */
+    @keyframes fade-in-keyframe {
+      from { opacity: 0; transform: translateY(8px); }
+      to { opacity: 1; transform: translateY(0); }
+    }
+    .fade-in-good { animation: fade-in-keyframe 0.4s cubic-bezier(0.16, 1, 0.3, 1); }
+    .ease-out-quart { transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1); }
+    .ease-out-expo { transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); }
+
+    /* ── PASS: safe transitions ── */
+    .transform-transition { transition: transform 0.3s ease-out; }
+    .opacity-transition { transition: opacity 0.2s ease; }
+    .color-transition { transition: color 0.15s ease, background-color 0.15s ease; }
+    .shadow-transition { transition: box-shadow 0.2s ease; }
+    .all-transition { transition: all 0.3s ease; }
+    .multi-safe-transition { transition: transform 0.3s ease, opacity 0.3s ease, box-shadow 0.2s ease; }
+  </style>
+</head>
+<body>
+  <div class="grid">
+    <!-- ════════════════════════════════════════════════════════════
+         LEFT COLUMN: should flag
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="flag">
+      <h2>Should flag</h2>
+
+      <h3>Bounce / elastic easing</h3>
+      <div class="demo bounce-animation">
+        <h4>CSS bounce animation</h4>
+        <p>animation: bounce 1s infinite</p>
+      </div>
+      <div class="demo elastic-transition">
+        <h4>Elastic cubic-bezier</h4>
+        <p>cubic-bezier(0.68, -0.55, 0.265, 1.55)</p>
+      </div>
+
+      <h3>Layout property transitions</h3>
+      <div class="demo width-transition">
+        <h4>transition: width</h4>
+        <p>Animating width causes layout thrash.</p>
+      </div>
+      <div class="demo height-transition">
+        <h4>transition: height</h4>
+        <p>Animating height causes layout thrash.</p>
+      </div>
+      <div class="demo padding-transition">
+        <h4>transition: padding</h4>
+        <p>Animating padding causes layout thrash.</p>
+      </div>
+      <div class="demo margin-transition">
+        <h4>transition: margin</h4>
+        <p>Animating margin causes layout thrash.</p>
+      </div>
+      <div class="demo max-height-transition">
+        <h4>transition: max-height</h4>
+        <p>Use grid-template-rows instead.</p>
+      </div>
+      <div class="demo multi-layout-transition">
+        <h4>transition: width, height</h4>
+        <p>Multiple layout properties.</p>
+      </div>
+      <div class="demo mixed-transition">
+        <h4>transition: width, opacity</h4>
+        <p>Layout property mixed with OK property.</p>
+      </div>
+      <div class="demo transition-property-width">
+        <h4>transition-property: width</h4>
+        <p>Longhand form.</p>
+      </div>
+    </div>
+
+    <!-- ════════════════════════════════════════════════════════════
+         RIGHT COLUMN: should pass
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="pass">
+      <h2>Should pass</h2>
+
+      <h3>Good easing</h3>
+      <div class="demo fade-in-good">
+        <h4>Smooth fade in</h4>
+        <p>animation: fadeIn with exponential ease-out</p>
+      </div>
+      <div class="demo ease-out-quart">
+        <h4>Ease-out quart</h4>
+        <p>cubic-bezier(0.25, 1, 0.5, 1) — smooth deceleration</p>
+      </div>
+      <div class="demo ease-out-expo">
+        <h4>Ease-out expo</h4>
+        <p>cubic-bezier(0.16, 1, 0.3, 1) — natural feel</p>
+      </div>
+
+      <h3>Safe transitions (transform / opacity / color only)</h3>
+      <div class="demo transform-transition">
+        <h4>transition: transform</h4>
+        <p>Transform is GPU-accelerated and safe.</p>
+      </div>
+      <div class="demo opacity-transition">
+        <h4>transition: opacity</h4>
+        <p>Opacity is GPU-accelerated and safe.</p>
+      </div>
+      <div class="demo color-transition">
+        <h4>transition: color, background-color</h4>
+        <p>Color transitions are paint-only, no layout.</p>
+      </div>
+      <div class="demo shadow-transition">
+        <h4>transition: box-shadow</h4>
+        <p>Shadow transitions are paint-only.</p>
+      </div>
+      <div class="demo all-transition">
+        <h4>transition: all</h4>
+        <p>Too common to flag — usually paired with transform/opacity.</p>
+      </div>
+      <div class="demo multi-safe-transition">
+        <h4>transition: transform, opacity, box-shadow</h4>
+        <p>Multiple safe properties combined.</p>
+      </div>
+    </div>
+  </div>
+  <script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/multifile/App.tsx 🔗

@@ -0,0 +1,14 @@
+// App that imports components -- anti-patterns are in the imported files
+import React from 'react';
+import { Card } from './Card';
+import './styles.css';
+
+export function App() {
+  return (
+    <main className="p-8">
+      <h1 className="text-3xl font-bold">Dashboard</h1>
+      <Card title="Revenue" value="$12,345" />
+      <Card title="Users" value="1,234" />
+    </main>
+  );
+}

tests/fixtures/antipatterns/multifile/Card.tsx 🔗

@@ -0,0 +1,16 @@
+// Component with anti-patterns
+import React from 'react';
+
+interface CardProps {
+  title: string;
+  value: string;
+}
+
+export function Card({ title, value }: CardProps) {
+  return (
+    <div className="border-l-4 border-blue-500 rounded-lg p-4 bg-white shadow">
+      <h3 className="text-purple-500 text-xl font-bold">{title}</h3>
+      <p className="text-2xl tabular-nums">{value}</p>
+    </div>
+  );
+}

tests/fixtures/antipatterns/overlay-positioning.html 🔗

@@ -0,0 +1,525 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Overlay Positioning Edge Cases</title>
+  <style>
+    body {
+      font-family: system-ui, sans-serif;
+      background: #f9fafb;
+      padding: 2rem;
+      max-width: 900px;
+      margin: 0 auto;
+    }
+    h1 { font-size: 2rem; margin-bottom: 0.5rem; }
+    h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }
+    .scenario { margin-bottom: 2rem; padding: 1rem; border: 1px dashed #d1d5db; border-radius: 8px; }
+    .scenario-label { font-size: 0.75rem; color: #6b7280; margin-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.05em; }
+
+    /* ============================================
+       1. TRANSFORM ANCESTOR
+       position:absolute overlays on body use document coords,
+       but getBoundingClientRect is viewport-relative.
+       Transforms on ancestors create new stacking contexts.
+       ============================================ */
+    .transform-container {
+      transform: translateY(0);
+      padding: 1rem;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .transform-rotate {
+      transform: rotate(0deg);
+      padding: 1rem;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .transform-scale {
+      transform: scale(1);
+      padding: 1rem;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .transform-nested-outer {
+      transform: translateX(0);
+      padding: 1rem;
+      background: #e5e7eb;
+      border-radius: 8px;
+    }
+    .transform-nested-inner {
+      transform: translateY(0);
+      padding: 1rem;
+      background: #d1d5db;
+      border-radius: 8px;
+    }
+
+    /* ============================================
+       2. CLOSED <details>
+       Elements inside collapsed details may report
+       a bounding rect but aren't visually present.
+       ============================================ */
+    details { margin-bottom: 1rem; }
+    summary { cursor: pointer; font-weight: 600; }
+
+    /* ============================================
+       3. STICKY / FIXED POSITIONING
+       Sticky elements change their containing block
+       behavior during scroll.
+       ============================================ */
+    .sticky-container {
+      height: 200px;
+      overflow-y: auto;
+      border: 1px solid #d1d5db;
+      border-radius: 8px;
+    }
+    .sticky-header {
+      position: sticky;
+      top: 0;
+      background: #1f2937;
+      color: white;
+      padding: 0.5rem 1rem;
+      z-index: 10;
+    }
+    .sticky-content {
+      padding: 1rem;
+    }
+
+    /* ============================================
+       4. OVERFLOW HIDDEN
+       Elements that extend beyond an overflow:hidden
+       parent are visually clipped but still in the DOM.
+       ============================================ */
+    .overflow-hidden-container {
+      overflow: hidden;
+      height: 60px;
+      border: 1px solid #d1d5db;
+      border-radius: 8px;
+      padding: 0.5rem 1rem;
+    }
+
+    /* ============================================
+       5. ABSOLUTE / RELATIVE POSITIONING
+       Elements that are offset from their normal flow
+       position via top/left.
+       ============================================ */
+    .relative-offset {
+      position: relative;
+      top: 20px;
+      left: 40px;
+      padding: 1rem;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .absolute-container {
+      position: relative;
+      height: 120px;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .absolute-child {
+      position: absolute;
+      bottom: 10px;
+      right: 10px;
+    }
+
+    /* ============================================
+       6. FLEXBOX / GRID CENTERING
+       Elements centered via flex/grid may have
+       unexpected positions vs. document flow.
+       ============================================ */
+    .flex-center {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      height: 120px;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .grid-center {
+      display: grid;
+      place-items: center;
+      height: 120px;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+
+    /* ============================================
+       7. WILL-CHANGE / CONTAIN / FILTER
+       These CSS properties create new containing
+       blocks, similar to transforms.
+       ============================================ */
+    .will-change-container {
+      will-change: transform;
+      padding: 1rem;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .contain-container {
+      contain: layout;
+      padding: 1rem;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .filter-container {
+      filter: brightness(1);
+      padding: 1rem;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .backdrop-filter-container {
+      backdrop-filter: blur(0px);
+      padding: 1rem;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+
+    /* ============================================
+       8. MARGIN COLLAPSE / NEGATIVE MARGINS
+       Elements with collapsed or negative margins
+       can shift from expected position.
+       ============================================ */
+    .negative-margin-container {
+      padding: 2rem;
+      background: #f3f4f6;
+      border-radius: 8px;
+    }
+    .negative-margin-child {
+      margin-top: -1rem;
+      margin-left: -1rem;
+    }
+
+    /* ============================================
+       Anti-pattern triggers (tiny-text, cramped, ai-color)
+       used across scenarios.
+       ============================================ */
+    .tiny { font-size: 10px; color: #374151; line-height: 1.4; }
+    .cramped-box { border: 1px solid #d1d5db; padding: 3px; font-size: 14px; border-radius: 4px; }
+    .ai-btn { background: linear-gradient(135deg, #8b5cf6, #6366f1); color: white; padding: 8px 16px; border: none; border-radius: 6px; font-size: 14px; cursor: pointer; }
+    .side-tab-card { border-left: 4px solid #8b5cf6; padding: 1rem; background: white; border-radius: 0 8px 8px 0; }
+  </style>
+</head>
+<body>
+
+<h1>Overlay Positioning Edge Cases</h1>
+<p>Each scenario places a detectable anti-pattern inside a layout context that can cause overlay misalignment.</p>
+
+<!-- ═══════════════════════════════════════════
+     1. TRANSFORM ANCESTORS
+     ═══════════════════════════════════════════ -->
+<h2>1. Transform Ancestors</h2>
+
+<div class="scenario">
+  <div class="scenario-label">1a. translateY(0) container</div>
+  <div class="transform-container">
+    <span class="tiny">This tiny text is inside a transform: translateY(0) container. The overlay should frame this text precisely.</span>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">1b. rotate(0deg) container</div>
+  <div class="transform-rotate">
+    <span class="tiny">Tiny text inside a transform: rotate(0deg) container.</span>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">1c. scale(1) container</div>
+  <div class="transform-scale">
+    <div class="cramped-box">Cramped text inside a transform: scale(1) container.</div>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">1d. Nested transforms</div>
+  <div class="transform-nested-outer">
+    <div class="transform-nested-inner">
+      <span class="tiny">Tiny text nested inside two levels of transformed parents.</span>
+    </div>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">1e. Transform + absolute child</div>
+  <div class="transform-container" style="position: relative; height: 100px;">
+    <div style="position: absolute; bottom: 8px; right: 8px;">
+      <span class="tiny">Absolutely positioned tiny text inside transformed parent.</span>
+    </div>
+  </div>
+</div>
+
+<!-- ═══════════════════════════════════════════
+     2. CLOSED <details>
+     ═══════════════════════════════════════════ -->
+<h2>2. Closed Details</h2>
+
+<div class="scenario">
+  <div class="scenario-label">2a. Closed details with anti-pattern inside</div>
+  <details>
+    <summary>Click to expand (closed by default)</summary>
+    <span class="tiny">This tiny text is hidden inside a closed details element. No overlay should appear for it.</span>
+    <div class="cramped-box">Cramped text also hidden inside closed details.</div>
+  </details>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">2b. Open details with anti-pattern inside</div>
+  <details open>
+    <summary>This details is open</summary>
+    <span class="tiny">This tiny text IS visible because details is open. Overlay should frame it correctly.</span>
+  </details>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">2c. Nested details (outer open, inner closed)</div>
+  <details open>
+    <summary>Outer details (open)</summary>
+    <p>Some visible content.</p>
+    <details>
+      <summary>Inner details (closed)</summary>
+      <span class="tiny">Hidden tiny text inside nested closed details.</span>
+    </details>
+  </details>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">2d. Transform inside closed details</div>
+  <details>
+    <summary>Closed with transform inside</summary>
+    <div class="transform-container">
+      <span class="tiny">Tiny text inside transform inside closed details. Double trouble.</span>
+    </div>
+  </details>
+</div>
+
+<!-- ═══════════════════════════════════════════
+     3. STICKY / FIXED
+     ═══════════════════════════════════════════ -->
+<h2>3. Sticky and Fixed Positioning</h2>
+
+<div class="scenario">
+  <div class="scenario-label">3a. Sticky header inside scrollable container</div>
+  <div class="sticky-container">
+    <div class="sticky-header">
+      <span class="tiny">Tiny text in a sticky header.</span>
+    </div>
+    <div class="sticky-content">
+      <p>Scroll this container to test sticky behavior.</p>
+      <p>More content to enable scrolling.</p>
+      <p>Even more content here.</p>
+      <div class="cramped-box">Cramped text below the sticky header.</div>
+      <p>Additional content.</p>
+      <p>More filler text.</p>
+    </div>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">3b. Fixed footer (stays at bottom of viewport)</div>
+  <p>The fixed footer at the bottom of the page has tiny text. Its overlay should stay fixed too.</p>
+</div>
+
+<!-- Actual fixed footer -- outside scenario container so it's truly fixed -->
+<div style="position: fixed; bottom: 0; left: 0; right: 0; background: #1f2937; color: white; padding: 0.5rem 1rem; z-index: 50;">
+  <span class="tiny">Tiny text in a fixed footer -- overlay should track this on scroll.</span>
+</div>
+
+<!-- ═══════════════════════════════════════════
+     4. OVERFLOW HIDDEN
+     ═══════════════════════════════════════════ -->
+<h2>4. Overflow Hidden</h2>
+
+<div class="scenario">
+  <div class="scenario-label">4a. Content clipped by overflow:hidden</div>
+  <div class="overflow-hidden-container">
+    <p>This paragraph is visible.</p>
+    <span class="tiny">This tiny text is below the overflow cutoff, clipped but still in DOM.</span>
+    <p>This content is also clipped away.</p>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">4b. overflow:hidden + transform ancestor</div>
+  <div class="transform-container">
+    <div class="overflow-hidden-container">
+      <p>Visible inside transform + overflow.</p>
+      <span class="tiny">Clipped tiny text inside a transformed overflow:hidden container.</span>
+    </div>
+  </div>
+</div>
+
+<!-- ═══════════════════════════════════════════
+     5. ABSOLUTE / RELATIVE OFFSETS
+     ═══════════════════════════════════════════ -->
+<h2>5. Position Offsets</h2>
+
+<div class="scenario">
+  <div class="scenario-label">5a. Relative position with top/left offset</div>
+  <div class="relative-offset">
+    <span class="tiny">This tiny text is shifted via position:relative + top/left offset.</span>
+  </div>
+  <div style="height: 30px;"></div><!-- spacer for the offset -->
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">5b. Absolute child in relative parent</div>
+  <div class="absolute-container">
+    <p>Normal flow content at top.</p>
+    <div class="absolute-child">
+      <span class="tiny">Absolutely positioned tiny text at bottom-right.</span>
+    </div>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">5c. Negative top offset</div>
+  <div style="padding-top: 2rem;">
+    <div style="position: relative; top: -1rem;">
+      <span class="tiny">Tiny text pulled upward via negative top offset.</span>
+    </div>
+  </div>
+</div>
+
+<!-- ═══════════════════════════════════════════
+     6. FLEX / GRID CENTERING
+     ═══════════════════════════════════════════ -->
+<h2>6. Flex and Grid Centering</h2>
+
+<div class="scenario">
+  <div class="scenario-label">6a. Flex-centered anti-pattern</div>
+  <div class="flex-center">
+    <span class="tiny">Centered tiny text inside a flex container.</span>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">6b. Grid-centered anti-pattern</div>
+  <div class="grid-center">
+    <div class="cramped-box">Cramped text inside a grid-centered container.</div>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">6c. Flex with transform</div>
+  <div class="flex-center transform-container">
+    <button class="ai-btn">AI Gradient Button in Flex + Transform</button>
+  </div>
+</div>
+
+<!-- ═══════════════════════════════════════════
+     7. WILL-CHANGE / CONTAIN / FILTER
+     ═══════════════════════════════════════════ -->
+<h2>7. Containing Block Creators</h2>
+
+<div class="scenario">
+  <div class="scenario-label">7a. will-change: transform</div>
+  <div class="will-change-container">
+    <span class="tiny">Tiny text inside a will-change: transform container.</span>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">7b. contain: layout</div>
+  <div class="contain-container">
+    <span class="tiny">Tiny text inside a contain: layout container.</span>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">7c. filter: brightness(1)</div>
+  <div class="filter-container">
+    <span class="tiny">Tiny text inside a filter container (creates containing block).</span>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">7d. backdrop-filter</div>
+  <div class="backdrop-filter-container">
+    <span class="tiny">Tiny text inside a backdrop-filter container.</span>
+  </div>
+</div>
+
+<!-- ═══════════════════════════════════════════
+     8. NEGATIVE MARGINS
+     ═══════════════════════════════════════════ -->
+<h2>8. Margin Collapse and Negative Margins</h2>
+
+<div class="scenario">
+  <div class="scenario-label">8a. Negative margin shifting element position</div>
+  <div class="negative-margin-container">
+    <div class="negative-margin-child">
+      <span class="tiny">Tiny text pulled out of its parent via negative margins.</span>
+    </div>
+  </div>
+</div>
+
+<!-- ═══════════════════════════════════════════
+     9. COMBINATION SCENARIOS
+     ═══════════════════════════════════════════ -->
+<h2>9. Combined Edge Cases</h2>
+
+<div class="scenario">
+  <div class="scenario-label">9a. Transform + sticky + overflow</div>
+  <div class="transform-container">
+    <div class="sticky-container">
+      <div class="sticky-header">
+        <span class="tiny">Tiny text in sticky header inside transform inside scrollable overflow.</span>
+      </div>
+      <div class="sticky-content">
+        <p>Scroll content line 1.</p>
+        <p>Scroll content line 2.</p>
+        <p>Scroll content line 3.</p>
+        <div class="cramped-box">Cramped box scrolled below sticky.</div>
+        <p>Scroll content line 4.</p>
+        <p>Scroll content line 5.</p>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">9b. Closed details + transform + flex</div>
+  <details>
+    <summary>Closed combo scenario</summary>
+    <div class="flex-center transform-container">
+      <span class="tiny">Triple-nested: closed details > flex > transform > tiny text.</span>
+    </div>
+  </details>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">9c. Grid + absolute + transform</div>
+  <div class="grid-center" style="position: relative; height: 150px;">
+    <div class="transform-container" style="position: absolute; top: 10px; left: 10px; right: 10px;">
+      <span class="tiny">Absolute inside grid inside transform: overlays must track all three contexts.</span>
+    </div>
+    <div style="position: absolute; bottom: 10px; right: 10px;">
+      <div class="cramped-box">Absolute cramped box at bottom-right of grid.</div>
+    </div>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">9d. Overflow hidden + absolute breakout</div>
+  <div style="position: relative; overflow: hidden; height: 80px; background: #f3f4f6; border-radius: 8px; padding: 1rem;">
+    <p>Visible content.</p>
+    <div style="position: absolute; bottom: -20px; left: 0; right: 0;">
+      <span class="tiny">Absolute-positioned tiny text that breaks out below overflow:hidden.</span>
+    </div>
+  </div>
+</div>
+
+<div class="scenario">
+  <div class="scenario-label">9e. Side-tab card inside transform + relative offset</div>
+  <div class="transform-container">
+    <div style="position: relative; left: 50px;">
+      <div class="side-tab-card">
+        This card has a side-tab border accent and is offset inside a transform container.
+      </div>
+    </div>
+  </div>
+</div>
+
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/partial-component.html 🔗

@@ -0,0 +1,11 @@
+<!-- This is a partial/component — no DOCTYPE, no <html>, no <head> -->
+<!-- Page-level checks (flat hierarchy, single font) should NOT run -->
+<!-- But element-level border checks SHOULD still run -->
+
+<div class="card" style="font-family: 'Inter', sans-serif;">
+  <div style="font-size: 14px; border-left: 4px solid #3b82f6; border-radius: 8px; padding: 1rem; background: white;">
+    <h3 style="font-size: 16px; font-weight: 600;">Card Title</h3>
+    <p style="font-size: 15px; color: #6b7280;">Card description with close font sizes.</p>
+  </div>
+</div>
+<script src="/js/detect-antipatterns-browser.js"></script>

tests/fixtures/antipatterns/quality.html 🔗

@@ -0,0 +1,227 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Quality (Typography & Readability) — Should Flag vs Should Pass</title>
+  <style>
+    /* Two-column fixture: left = should flag, right = should pass.
+       Covers the typography-quality rules that need real browser layout
+       (line-length, tight-leading, tiny-text, justified-text, all-caps-body,
+       wide-tracking, skipped-heading). All are browser-only — see
+       tests/detect-antipatterns-browser.test.mjs for assertions. */
+    body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #0f172a; line-height: 1.6; }
+    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1280px; margin: 0 auto; }
+    .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
+    .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }
+    .case { margin-bottom: 16px; padding: 16px; background: white; border: 1px solid #e2e8f0; border-radius: 8px; }
+    .case-label { display: block; font-size: 12px; color: #64748b; margin-bottom: 6px; font-style: italic; }
+
+    /* ── FLAG: typography quality issues ── */
+
+    /* Line length: 12px body text fits >85 chars/line in a ~528px column.
+       12px is not below the tiny-text 12px floor (rule fires at <12px). */
+    .long-lines {
+      font-size: 12px;
+      line-height: 1.6;
+    }
+
+    /* Tight leading */
+    .tight-leading {
+      font-size: 16px;
+      line-height: 1.0;
+      max-width: 40em;
+    }
+
+    /* Tiny body text */
+    .tiny-text {
+      font-size: 10px;
+      line-height: 1.6;
+      max-width: 40em;
+    }
+    .tiny-text-11 {
+      font-size: 11px;
+      line-height: 1.6;
+      max-width: 40em;
+    }
+
+    /* Justified text without hyphens */
+    .justified-no-hyphens {
+      text-align: justify;
+      max-width: 30em;
+      font-size: 16px;
+      line-height: 1.6;
+    }
+
+    /* All-caps long body */
+    .all-caps-body {
+      text-transform: uppercase;
+      font-size: 16px;
+      max-width: 40em;
+      line-height: 1.6;
+    }
+
+    /* Wide letter spacing on body */
+    .wide-tracking-body {
+      letter-spacing: 0.15em;
+      font-size: 16px;
+      max-width: 40em;
+      line-height: 1.6;
+    }
+
+    /* ── PASS: comfortable typography ── */
+
+    .good-measure {
+      max-width: 65ch;
+      font-size: 16px;
+      line-height: 1.6;
+    }
+
+    .good-leading {
+      font-size: 16px;
+      line-height: 1.6;
+      max-width: 40em;
+    }
+
+    .good-text-size {
+      font-size: 16px;
+      line-height: 1.6;
+      max-width: 40em;
+    }
+
+    /* Justified text WITH hyphens — fine */
+    .justified-with-hyphens {
+      text-align: justify;
+      hyphens: auto;
+      -webkit-hyphens: auto;
+      max-width: 30em;
+      font-size: 16px;
+      line-height: 1.6;
+    }
+
+    /* Short label in all-caps (passes — under 30 chars) */
+    .label-caps {
+      text-transform: uppercase;
+      font-size: 12px;
+      font-weight: 600;
+      letter-spacing: 0.1em;
+      color: #475569;
+    }
+
+    /* Wide tracking on a short uppercase label (passes — both conditions excluded) */
+    .label-tracking {
+      letter-spacing: 0.15em;
+      text-transform: uppercase;
+      font-size: 12px;
+      font-weight: 600;
+      color: #475569;
+    }
+  </style>
+</head>
+<body>
+  <div class="grid">
+
+    <!-- ════════════════════════════════════════════════════════════
+         LEFT COLUMN: should flag
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="flag">
+      <h2>Should flag</h2>
+
+      <h3>Line length too long</h3>
+      <div class="case">
+        <span class="case-label">no max-width on a paragraph</span>
+        <p class="long-lines">This paragraph has no max-width constraint at all, which means on a wide monitor or ultrawide display, each line of text can stretch to 150 or even 200 characters wide. Research consistently shows that line lengths beyond 75 characters significantly reduce reading speed and comprehension. The eye has to travel too far to find the beginning of the next line, causing readers to lose their place. A simple max-width of 65ch to 75ch on the paragraph or its container would fix this entirely.</p>
+      </div>
+
+      <h3>Tight line height</h3>
+      <div class="case">
+        <span class="case-label">line-height: 1.0</span>
+        <p class="tight-leading">This paragraph has a line-height of 1.0, which means the lines are touching. Multi-line body text needs breathing room between lines for readability. A line-height of 1.5 to 1.7 is generally recommended for body text.</p>
+      </div>
+
+      <h3>Tiny body text</h3>
+      <div class="case">
+        <span class="case-label">10px body text</span>
+        <p class="tiny-text">This body text is only 10px. While this might be fine for a disclaimer or legal footnote, it's too small for primary content that users need to actually read. Aim for at least 14px for body content, 16px is ideal.</p>
+      </div>
+
+      <h3>Justified text without hyphens</h3>
+      <div class="case">
+        <span class="case-label">text-align: justify, no hyphens: auto</span>
+        <p class="justified-no-hyphens">This paragraph uses text-align: justify, which forces each line to stretch to fill the full width. Without hyphenation support, this creates uneven gaps between words known as "rivers of white space" that flow vertically through the text. Left-aligned text is almost always more readable on the web.</p>
+      </div>
+
+      <h3>All-caps body text</h3>
+      <div class="case">
+        <span class="case-label">text-transform: uppercase on a long passage</span>
+        <p class="all-caps-body">This entire paragraph is in uppercase via text-transform. While all-caps works for short labels, headings, or navigation items, longer body text in uppercase is significantly harder to read because we lose the word shape cues that come from ascenders and descenders in mixed-case text.</p>
+      </div>
+
+      <h3>Wide letter spacing on body text</h3>
+      <div class="case">
+        <span class="case-label">letter-spacing: 0.15em on body text</span>
+        <p class="wide-tracking-body">This body text has letter-spacing: 0.15em applied to it. While subtle tracking adjustments can improve readability for headings or all-caps text, adding significant letter spacing to body text actually makes it harder to read by disrupting natural character groupings.</p>
+      </div>
+
+      <h3>Skipped heading levels</h3>
+      <div class="case">
+        <span class="case-label">h1 → h3 (missing h2)</span>
+        <h1 style="font-size: 20px; margin: 0 0 4px;">Top heading</h1>
+        <h3 style="font-size: 16px; margin: 0;">Skips straight to h3</h3>
+      </div>
+    </div>
+
+    <!-- ════════════════════════════════════════════════════════════
+         RIGHT COLUMN: should pass
+         ═══════════════════════════════════════════════════════════ -->
+    <div class="col" data-col="pass">
+      <h2>Should pass</h2>
+
+      <h3>Comfortable line length</h3>
+      <div class="case">
+        <span class="case-label">max-width: 65ch on the paragraph</span>
+        <p class="good-measure">This paragraph has a max-width of 65ch, keeping the line length comfortable for reading. The eye can easily track from the end of one line to the beginning of the next without losing its place.</p>
+      </div>
+
+      <h3>Comfortable line height</h3>
+      <div class="case">
+        <span class="case-label">line-height: 1.6</span>
+        <p class="good-leading">This paragraph has a line-height of 1.6, which gives multi-line text plenty of room to breathe and improves the rhythm of the page.</p>
+      </div>
+
+      <h3>Comfortable body text size</h3>
+      <div class="case">
+        <span class="case-label">16px body text</span>
+        <p class="good-text-size">This is 16px body text — the recommended baseline for comfortable reading on modern displays.</p>
+      </div>
+
+      <h3>Justified text with hyphens</h3>
+      <div class="case">
+        <span class="case-label">text-align: justify + hyphens: auto</span>
+        <p class="justified-with-hyphens">When justified text is paired with hyphens: auto, the browser can break long words across lines, eliminating the rivers of white space and making the justification look intentional rather than awkward.</p>
+      </div>
+
+      <h3>Short label in all-caps</h3>
+      <div class="case">
+        <span class="case-label">short label, uppercase</span>
+        <span class="label-caps">Featured</span>
+      </div>
+
+      <h3>Wide tracking on a short uppercase label</h3>
+      <div class="case">
+        <span class="case-label">letter-spacing on a short uppercase label</span>
+        <span class="label-tracking">Beta</span>
+      </div>
+
+      <h3>Proper heading hierarchy</h3>
+      <div class="case">
+        <span class="case-label">h1 → h2 → h3</span>
+        <h1 style="font-size: 20px; margin: 0 0 4px;">Top heading</h1>
+        <h2 style="font-size: 18px; margin: 0 0 4px;">Second level</h2>
+        <h3 style="font-size: 16px; margin: 0;">Third level</h3>
+      </div>
+    </div>
+  </div>
+  <script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/should-flag.html 🔗

@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Anti-Patterns That Should Be Flagged</title>
+  <script src="https://cdn.tailwindcss.com"></script>
+  <style>
+    body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }
+    h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }
+    h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }
+    p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }
+    .cards { display: grid; gap: 1rem; max-width: 28rem; }
+    .card { background: white; padding: 1rem; border-radius: 0.375rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
+    .card h3 { font-weight: 600; color: #111827; font-size: 0.875rem; }
+    .card p { font-size: 0.8125rem; color: #6b7280; margin-top: 0.25rem; }
+
+    /* CSS side-tab variants */
+    .card-shorthand-left { border-left: 4px solid #3b82f6; }
+    .card-shorthand-right { border-right: 5px solid #8b5cf6; }
+    .card-longhand-left { border-left-width: 3px; border-left-style: solid; border-left-color: #10b981; }
+    .card-longhand-right { border-right-width: 6px; border-right-style: solid; border-right-color: #ef4444; }
+    .card-logical-start { border-inline-start: 4px solid #f59e0b; }
+    .card-logical-end { border-inline-end: 3px solid #ec4899; }
+    .card-logical-start-width { border-inline-start-width: 5px; border-inline-start-style: solid; border-inline-start-color: #06b6d4; }
+
+    /* CSS top/bottom + border-radius */
+    .card-css-top { border-radius: 12px; border-top: 4px solid #3b82f6; }
+    .card-css-bottom { border-radius: 12px; border-bottom: 3px solid #8b5cf6; }
+  </style>
+</head>
+<body>
+  <h1>Anti-Patterns: Should Flag</h1>
+  <p class="intro">Every example on this page should be detected by the anti-pattern scanner.</p>
+
+  <!-- TAILWIND SIDE-TAB -->
+  <h2>Tailwind Side-Tab</h2>
+  <div class="cards">
+    <div class="border-l-4 border-blue-500 bg-white p-4 rounded-r shadow-sm">
+      <h3>border-l-4 + rounded-r</h3>
+      <p>The classic AI tell.</p>
+    </div>
+    <div class="border-l-2 border-emerald-500 bg-white p-4 rounded-r shadow-sm">
+      <h3>border-l-2 + rounded-r</h3>
+      <p>Thin but still recognizable with rounded corners.</p>
+    </div>
+    <div class="border-r-4 border-purple-500 bg-white p-4 rounded-l shadow-sm">
+      <h3>border-r-4 + rounded-l</h3>
+      <p>Right side variant.</p>
+    </div>
+    <div class="border-s-3 border-amber-500 bg-white p-4 rounded-r shadow-sm">
+      <h3>border-s-3 + rounded-r</h3>
+      <p>Logical inline-start.</p>
+    </div>
+    <div class="border-e-8 border-red-500 bg-white p-4 rounded-l shadow-sm">
+      <h3>border-e-8 + rounded-l</h3>
+      <p>Logical inline-end, extra thick.</p>
+    </div>
+  </div>
+
+  <!-- CSS SIDE-TAB -->
+  <h2>CSS Side-Tab</h2>
+  <div class="cards">
+    <div class="card card-shorthand-left">
+      <h3>border-left: 4px solid</h3>
+      <p>CSS shorthand.</p>
+    </div>
+    <div class="card card-shorthand-right">
+      <h3>border-right: 5px solid</h3>
+      <p>CSS shorthand right.</p>
+    </div>
+    <div class="card card-longhand-left">
+      <h3>border-left-width: 3px</h3>
+      <p>CSS longhand.</p>
+    </div>
+    <div class="card card-longhand-right">
+      <h3>border-right-width: 6px</h3>
+      <p>CSS longhand right.</p>
+    </div>
+    <div class="card card-logical-start">
+      <h3>border-inline-start: 4px solid</h3>
+      <p>CSS logical start.</p>
+    </div>
+    <div class="card card-logical-end">
+      <h3>border-inline-end: 3px solid</h3>
+      <p>CSS logical end.</p>
+    </div>
+    <div class="card card-logical-start-width">
+      <h3>border-inline-start-width: 5px</h3>
+      <p>CSS logical longhand.</p>
+    </div>
+  </div>
+
+  <!-- TOP/BOTTOM + ROUNDED -->
+  <h2>Top/Bottom + Rounded</h2>
+  <div class="cards">
+    <div class="border-t-4 border-blue-500 rounded-lg bg-white p-4 shadow-sm">
+      <h3 class="font-semibold text-sm">border-t-4 + rounded-lg</h3>
+      <p class="text-sm text-gray-600">Top accent on rounded card.</p>
+    </div>
+    <div class="border-b-4 border-purple-500 rounded-xl bg-white p-4 shadow-sm">
+      <h3 class="font-semibold text-sm">border-b-4 + rounded-xl</h3>
+      <p class="text-sm text-gray-600">Bottom accent on rounded card.</p>
+    </div>
+    <div class="border-t-2 border-emerald-500 rounded-md bg-white p-4 shadow-sm">
+      <h3 class="font-semibold text-sm">border-t-2 + rounded-md</h3>
+      <p class="text-sm text-gray-600">Even thin top border on rounded.</p>
+    </div>
+    <div class="card card-css-top">
+      <h3>CSS border-top + border-radius</h3>
+      <p>Top border from style block.</p>
+    </div>
+    <div class="card card-css-bottom">
+      <h3>CSS border-bottom + border-radius</h3>
+      <p>Bottom border from style block.</p>
+    </div>
+  </div>
+  <!-- DARK MODE VARIANTS -->
+  <h2>Dark Mode</h2>
+  <div class="cards">
+    <div class="border-l-4 border-cyan-400 rounded-lg bg-gray-800 p-4 shadow-sm">
+      <h3 class="font-semibold text-sm text-white">Dark card + border-l-4 + rounded</h3>
+      <p class="text-sm text-gray-400">Dark background doesn't make the side-tab OK.</p>
+    </div>
+    <div class="border-t-2 border-violet-500 rounded-xl bg-slate-900 p-4 shadow-sm">
+      <h3 class="font-semibold text-sm text-white">Dark card + border-t-2 + rounded</h3>
+      <p class="text-sm text-gray-400">Top accent on dark rounded card.</p>
+    </div>
+    <div style="background: #1f2937; padding: 1rem; border-radius: 12px; border-left: 4px solid #f472b6; max-width: 28rem;">
+      <h3 style="font-weight: 600; font-size: 0.875rem; color: white;">Dark CSS card + side border + radius</h3>
+      <p style="font-size: 0.8125rem; color: #9ca3af;">Inline dark card with side-tab.</p>
+    </div>
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/should-pass.html 🔗

@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Clean Patterns — Should NOT Flag</title>
+  <style>
+    body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; }
+    h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }
+    h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }
+    p.intro { color: #6b7280; margin-bottom: 2rem; max-width: 36rem; }
+    .section { max-width: 28rem; }
+    .card-rounded { background: white; padding: 1.5rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }
+    .card-rounded h3 { font-weight: 600; color: #111827; }
+    .card-rounded p { font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem; }
+    .card-flat { background: white; padding: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); max-width: 28rem; margin-bottom: 1rem; }
+    .badge { display: inline-block; font-size: 0.75rem; padding: 0.125rem 0.5rem; border-radius: 9999px; background: #dbeafe; color: #1d4ed8; }
+
+    /* Below-threshold side borders without radius */
+    .thin-left { border-left: 1px solid #3b82f6; }
+    .thin-right { border-right: 2px solid #8b5cf6; }
+  </style>
+</head>
+<body>
+  <h1>Clean Patterns: Should Pass</h1>
+  <p class="intro">None of these should be flagged. They're all intentional, well-established web patterns.</p>
+
+  <!-- CLEAN CARDS -->
+  <h2>Clean Cards</h2>
+  <div class="section">
+    <div class="card-rounded" style="border: 1px solid #e5e7eb;">
+      <h3>Full border card</h3>
+      <p>Subtle 1px border all around. Clean and intentional.</p>
+    </div>
+
+    <div style="max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-top: 3px solid #3b82f6; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
+      <h3 style="font-weight: 600; color: #111827;">Top border, no radius</h3>
+      <p style="font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;">Top accent without rounded corners is a clean section divider.</p>
+    </div>
+
+    <div style="max-width: 28rem; margin-bottom: 1rem; padding: 1.5rem; background: white; border-bottom: 2px solid #10b981; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
+      <h3 style="font-weight: 600; color: #111827;">Bottom border, no radius</h3>
+      <p style="font-size: 0.875rem; color: #6b7280; margin-top: 0.25rem;">Bottom accent without rounded corners is also clean.</p>
+    </div>
+
+    <div class="card-rounded">
+      <span class="badge">New</span>
+      <h3>No border at all</h3>
+      <p>Just a shadow. Simple and effective.</p>
+    </div>
+  </div>
+
+  <!-- BELOW THRESHOLD -->
+  <h2>Below Threshold (Thin, No Radius)</h2>
+  <div class="section">
+    <div class="card-flat thin-left">
+      <p>border-left: 1px solid, no radius — not flagged</p>
+    </div>
+    <div class="card-flat thin-right">
+      <p>border-right: 2px solid, no radius — not flagged</p>
+    </div>
+    <div class="card-flat" style="border-left: 1px solid #10b981;">
+      <p>1px inline border-left, no radius — not flagged</p>
+    </div>
+  </div>
+  <!-- DARK MODE CLEAN -->
+  <h2>Dark Mode (Clean)</h2>
+  <div class="section">
+    <div style="background: #1f2937; padding: 1.5rem; border-radius: 0.5rem; border: 1px solid #374151; max-width: 28rem; margin-bottom: 1rem;">
+      <h3 style="font-weight: 600; color: white;">Dark card, full border</h3>
+      <p style="font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;">Uniform 1px border all around. Clean.</p>
+    </div>
+    <div style="background: #111827; padding: 1.5rem; max-width: 28rem; margin-bottom: 1rem; border-bottom: 2px solid #6366f1;">
+      <h3 style="font-weight: 600; color: white;">Dark section, bottom border, no radius</h3>
+      <p style="font-size: 0.875rem; color: #9ca3af; margin-top: 0.25rem;">Bottom accent without radius is fine.</p>
+    </div>
+    <div style="background: #1e293b; padding: 1.5rem; border-radius: 0.5rem; max-width: 28rem; margin-bottom: 1rem; box-shadow: 0 4px 6px rgba(0,0,0,0.3);">
+      <h3 style="font-weight: 600; color: white;">Dark card, no border</h3>
+      <p style="font-size: 0.875rem; color: #94a3b8; margin-top: 0.25rem;">Shadow only. Clean.</p>
+    </div>
+  </div>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/svelte-should-flag.svelte 🔗

@@ -0,0 +1,33 @@
+<script>
+  export let title = '';
+  export let items = [];
+</script>
+
+<div class="sidebar border-r-4 border-violet-500 rounded-lg">
+  <h3 class="text-purple-500 text-3xl font-bold">{title}</h3>
+  <ul>
+    {#each items as item}
+      <li class="animate-bounce">{item.name}</li>
+    {/each}
+  </ul>
+</div>
+
+<style>
+  .sidebar {
+    border-right: 4px solid #8b5cf6;
+    border-radius: 16px;
+    font-family: 'Roboto', sans-serif;
+    background: #000000;
+  }
+
+  .sidebar h3 {
+    background: linear-gradient(135deg, #a855f7, #06b6d4);
+    -webkit-background-clip: text;
+    background-clip: text;
+  }
+
+  .animated-item {
+    animation: elastic 0.6s ease-out;
+    transition: height 0.4s ease;
+  }
+</style>

tests/fixtures/antipatterns/svelte-should-pass.svelte 🔗

@@ -0,0 +1,20 @@
+<script>
+  export let title = '';
+  export let items = [];
+</script>
+
+<div class="sidebar rounded-lg ring-1 ring-gray-200 p-4">
+  <h3 class="text-lg font-semibold text-gray-900">{title}</h3>
+  <ul>
+    {#each items as item}
+      <li class="py-2">{item.name}</li>
+    {/each}
+  </ul>
+</div>
+
+<style>
+  .sidebar {
+    font-family: 'Geist', system-ui, sans-serif;
+    background: #fafafa;
+  }
+</style>

tests/fixtures/antipatterns/typography-should-flag.html 🔗

@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Typography Anti-Patterns — Should Flag</title>
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
+  <style>
+    body {
+      font-family: 'Inter', sans-serif;
+      background: #f9fafb;
+      padding: 2rem;
+    }
+    h1 { font-size: 18px; font-weight: 700; margin-bottom: 0.5rem; }
+    h2 { font-size: 16px; font-weight: 600; margin: 2rem 0 0.75rem; color: #6b7280; }
+    h3 { font-size: 15px; font-weight: 600; }
+    p { font-size: 14px; color: #6b7280; margin-top: 0.25rem; }
+    .caption { font-size: 13px; color: #9ca3af; }
+  </style>
+</head>
+<body>
+  <h1>Typography Anti-Patterns</h1>
+  <p>This page triggers three typography detections:</p>
+
+  <h2>1. Overused Font</h2>
+  <p>Inter is loaded from Google Fonts and set as the only font-family. It's the most common AI default.</p>
+
+  <h2>2. Single Font</h2>
+  <p>There's no second font for headings or display text. Everything uses Inter — no typographic variety.</p>
+
+  <h2>3. Flat Type Hierarchy</h2>
+  <p>The font sizes are 13px, 14px, 15px, 16px, 18px — all crammed into a 5px range. No visual contrast between heading and body.</p>
+  <p class="caption">This caption is barely distinguishable from body text.</p>
+
+  <h3>A Subheading</h3>
+  <p>Can you tell this is a subheading? Exactly.</p>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/typography-should-pass.html 🔗

@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Typography — Clean Patterns</title>
+  <link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600&family=Fraunces:opsz,wght@9..144,400;9..144,700&display=swap" rel="stylesheet">
+  <style>
+    body {
+      font-family: 'Instrument Sans', system-ui, sans-serif;
+      background: #f9fafb;
+      padding: 2rem;
+      font-size: 16px;
+      color: #111827;
+    }
+    h1 {
+      font-family: 'Fraunces', Georgia, serif;
+      font-size: 48px;
+      font-weight: 700;
+      margin-bottom: 0.5rem;
+    }
+    h2 {
+      font-family: 'Fraunces', Georgia, serif;
+      font-size: 32px;
+      font-weight: 700;
+      margin: 2rem 0 0.75rem;
+    }
+    h3 { font-size: 24px; font-weight: 600; }
+    p { font-size: 16px; color: #6b7280; margin-top: 0.25rem; }
+    .caption { font-size: 12px; color: #6b7280; }
+  </style>
+</head>
+<body>
+  <h1>Good Typography</h1>
+  <p>This page uses distinctive fonts, proper pairing, and strong hierarchy.</p>
+
+  <h2>Two Font Families</h2>
+  <p>Fraunces (serif) for headings, Instrument Sans for body. Clear contrast in both structure and personality.</p>
+
+  <h3>Strong Size Hierarchy</h3>
+  <p>Sizes range from 12px to 48px — a 4:1 ratio with clear visual steps.</p>
+  <p class="caption">Caption text is clearly distinct from body.</p>
+<script src="/js/detect-antipatterns-browser.js"></script>
+</body>
+</html>

tests/fixtures/antipatterns/vue-should-flag.vue 🔗

@@ -0,0 +1,36 @@
+<template>
+  <div class="card border-l-4 border-indigo-500 rounded-xl p-6">
+    <h2 class="text-purple-600 text-2xl font-bold">{{ title }}</h2>
+    <p class="text-gray-400">{{ description }}</p>
+    <button class="animate-bounce mt-4 px-4 py-2 bg-indigo-500 text-white rounded">
+      Get Started
+    </button>
+  </div>
+</template>
+
+<script setup>
+defineProps({
+  title: String,
+  description: String,
+});
+</script>
+
+<style scoped>
+.card {
+  border-left: 4px solid #6366f1;
+  border-radius: 12px;
+  font-family: 'Inter', sans-serif;
+}
+
+.card h2 {
+  background: linear-gradient(to right, #a855f7, #06b6d4);
+  -webkit-background-clip: text;
+  background-clip: text;
+  color: transparent;
+}
+
+.highlight {
+  animation: bounce 1s infinite;
+  transition: width 0.3s ease;
+}
+</style>

tests/fixtures/antipatterns/vue-should-pass.vue 🔗

@@ -0,0 +1,23 @@
+<template>
+  <div class="card rounded-xl p-6 ring-1 ring-gray-200">
+    <h2 class="text-lg font-semibold text-gray-900">{{ title }}</h2>
+    <p class="text-gray-600 mt-2">{{ description }}</p>
+    <button class="mt-4 px-4 py-2 bg-teal-600 text-white rounded-md">
+      Get Started
+    </button>
+  </div>
+</template>
+
+<script setup>
+defineProps({
+  title: String,
+  description: String,
+});
+</script>
+
+<style scoped>
+.card {
+  font-family: 'Geist', system-ui, sans-serif;
+  background: #fff;
+}
+</style>

tests/lib/transformers/factory.test.js 🔗

@@ -0,0 +1,276 @@
+import { describe, test, expect, beforeEach, afterEach, mock } from 'bun:test';
+import fs from 'fs';
+import path from 'path';
+import { createTransformer } from '../../../scripts/lib/transformers/factory.js';
+import { parseFrontmatter } from '../../../scripts/lib/utils.js';
+
+const TEST_DIR = path.join(process.cwd(), 'test-tmp-factory');
+
+// Minimal config using 'cursor' as provider (has existing PROVIDER_PLACEHOLDERS)
+const baseConfig = {
+  provider: 'cursor',
+  configDir: '.test',
+  displayName: 'Test Provider',
+  frontmatterFields: [],
+};
+
+describe('createTransformer factory', () => {
+  beforeEach(() => {
+    if (fs.existsSync(TEST_DIR)) {
+      fs.rmSync(TEST_DIR, { recursive: true, force: true });
+    }
+  });
+
+  afterEach(() => {
+    if (fs.existsSync(TEST_DIR)) {
+      fs.rmSync(TEST_DIR, { recursive: true, force: true });
+    }
+  });
+
+  test('should create correct directory structure', () => {
+    const transform = createTransformer(baseConfig);
+    transform([], TEST_DIR);
+    expect(fs.existsSync(path.join(TEST_DIR, 'cursor/.test/skills'))).toBe(true);
+  });
+
+  test('should always emit name and description', () => {
+    const transform = createTransformer(baseConfig);
+    const skills = [{ name: 'test', description: 'A test skill', body: 'Body.' }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    const parsed = parseFrontmatter(content);
+    expect(parsed.frontmatter.name).toBe('test');
+    expect(parsed.frontmatter.description).toBe('A test skill');
+    expect(parsed.body).toBe('Body.');
+  });
+
+  test('should only emit allowlisted fields', () => {
+    const config = { ...baseConfig, frontmatterFields: ['license'] };
+    const transform = createTransformer(config);
+    const skills = [{
+      name: 'test',
+      description: 'Test',
+      license: 'MIT',
+      compatibility: 'all',
+      metadata: 'meta',
+      body: 'Body'
+    }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    const parsed = parseFrontmatter(content);
+    expect(parsed.frontmatter.license).toBe('MIT');
+    expect(parsed.frontmatter.compatibility).toBeUndefined();
+    expect(parsed.frontmatter.metadata).toBeUndefined();
+  });
+
+  test('should skip empty optional fields', () => {
+    const config = { ...baseConfig, frontmatterFields: ['license'] };
+    const transform = createTransformer(config);
+    const skills = [{ name: 'test', description: 'Test', license: '', body: 'Body' }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    const parsed = parseFrontmatter(content);
+    expect(parsed.frontmatter.license).toBeUndefined();
+  });
+
+  test('should emit user-invocable as true when skill is user-invocable', () => {
+    const config = { ...baseConfig, frontmatterFields: ['user-invocable'] };
+    const transform = createTransformer(config);
+    const skills = [{ name: 'test', description: 'Test', userInvocable: true, body: 'Body' }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    const parsed = parseFrontmatter(content);
+    expect(parsed.frontmatter['user-invocable']).toBe(true);
+  });
+
+  test('should not emit user-invocable when skill is not user-invocable', () => {
+    const config = { ...baseConfig, frontmatterFields: ['user-invocable'] };
+    const transform = createTransformer(config);
+    const skills = [{ name: 'test', description: 'Test', userInvocable: false, body: 'Body' }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    const parsed = parseFrontmatter(content);
+    expect(parsed.frontmatter['user-invocable']).toBeUndefined();
+  });
+
+  test('should emit argument-hint only when user-invocable', () => {
+    const config = { ...baseConfig, frontmatterFields: ['argument-hint'] };
+    const transform = createTransformer(config);
+
+    // User-invocable with hint
+    const skills1 = [{ name: 'test', description: 'Test', userInvocable: true, argumentHint: '[target]', body: 'Body' }];
+    transform(skills1, TEST_DIR);
+    let content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    let parsed = parseFrontmatter(content);
+    expect(parsed.frontmatter['argument-hint']).toBe('[target]');
+
+    // Non-user-invocable with hint
+    fs.rmSync(TEST_DIR, { recursive: true, force: true });
+    const skills2 = [{ name: 'test', description: 'Test', userInvocable: false, argumentHint: '[target]', body: 'Body' }];
+    transform(skills2, TEST_DIR);
+    content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    parsed = parseFrontmatter(content);
+    expect(parsed.frontmatter['argument-hint']).toBeUndefined();
+  });
+
+  test('should apply bodyTransform after placeholder replacement', () => {
+    const config = {
+      ...baseConfig,
+      bodyTransform: (body) => body.replace(/PLACEHOLDER/, 'TRANSFORMED'),
+    };
+    const transform = createTransformer(config);
+    const skills = [{ name: 'test', description: 'Test', body: 'PLACEHOLDER content' }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    expect(content).toContain('TRANSFORMED content');
+  });
+
+  test('should support prefix option', () => {
+    const transform = createTransformer(baseConfig);
+    const skills = [{ name: 'audit', description: 'Audit', userInvocable: true, body: 'Body' }];
+    transform(skills, TEST_DIR, { prefix: 'i-', outputSuffix: '-prefixed' });
+
+    const outputPath = path.join(TEST_DIR, 'cursor-prefixed/.test/skills/i-audit/SKILL.md');
+    expect(fs.existsSync(outputPath)).toBe(true);
+    const content = fs.readFileSync(outputPath, 'utf-8');
+    expect(content).toContain('name: i-audit');
+  });
+
+  test('should copy reference files', () => {
+    const transform = createTransformer(baseConfig);
+    const skills = [{
+      name: 'test',
+      description: 'Test',
+      body: 'Body',
+      references: [
+        { name: 'ref1', content: 'Reference 1 content', filePath: '/fake/ref1.md' },
+        { name: 'ref2', content: 'Reference 2 content', filePath: '/fake/ref2.md' },
+      ]
+    }];
+    transform(skills, TEST_DIR);
+
+    expect(fs.existsSync(path.join(TEST_DIR, 'cursor/.test/skills/test/reference/ref1.md'))).toBe(true);
+    expect(fs.existsSync(path.join(TEST_DIR, 'cursor/.test/skills/test/reference/ref2.md'))).toBe(true);
+    const ref1 = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/reference/ref1.md'), 'utf-8');
+    expect(ref1).toBe('Reference 1 content');
+  });
+
+  test('should clean existing directory before writing', () => {
+    const transform = createTransformer(baseConfig);
+    const existingDir = path.join(TEST_DIR, 'cursor/.test/skills/old');
+    fs.mkdirSync(existingDir, { recursive: true });
+    fs.writeFileSync(path.join(existingDir, 'SKILL.md'), 'old');
+
+    const skills = [{ name: 'new', description: 'New', body: 'New' }];
+    transform(skills, TEST_DIR);
+
+    expect(fs.existsSync(path.join(TEST_DIR, 'cursor/.test/skills/old/SKILL.md'))).toBe(false);
+    expect(fs.existsSync(path.join(TEST_DIR, 'cursor/.test/skills/new/SKILL.md'))).toBe(true);
+  });
+
+  test('should log correct summary', () => {
+    const consoleMock = mock(() => {});
+    const originalLog = console.log;
+    console.log = consoleMock;
+
+    const transform = createTransformer(baseConfig);
+    const skills = [
+      { name: 's1', description: 'Test', userInvocable: true, body: 'body' },
+      { name: 's2', description: 'Test', userInvocable: false, body: 'body' }
+    ];
+    transform(skills, TEST_DIR);
+
+    console.log = originalLog;
+    expect(consoleMock).toHaveBeenCalledWith(expect.stringContaining('✓ Test Provider:'));
+    expect(consoleMock).toHaveBeenCalledWith(expect.stringContaining('2 skills'));
+    expect(consoleMock).toHaveBeenCalledWith(expect.stringContaining('1 user-invocable'));
+  });
+
+  test('should handle empty skills array', () => {
+    const transform = createTransformer(baseConfig);
+    transform([], TEST_DIR);
+
+    const skillDirs = fs.readdirSync(path.join(TEST_DIR, 'cursor/.test/skills'));
+    expect(skillDirs).toHaveLength(0);
+  });
+
+  test('should replace {{model}} placeholder', () => {
+    const transform = createTransformer(baseConfig);
+    const skills = [{ name: 'test', description: 'Test', body: 'Ask {{model}} for help.' }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    expect(content).toContain('Ask the model for help.');
+  });
+
+  test('should replace {{config_file}} placeholder', () => {
+    const transform = createTransformer(baseConfig);
+    const skills = [{ name: 'test', description: 'Test', body: 'See {{config_file}}.' }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    expect(content).toContain('See .cursorrules.');
+  });
+
+  test('should handle multiple skills', () => {
+    const transform = createTransformer(baseConfig);
+    const skills = [
+      { name: 'skill1', description: 'Skill 1', body: 'Body 1' },
+      { name: 'skill2', description: 'Skill 2', body: 'Body 2' },
+    ];
+    transform(skills, TEST_DIR);
+
+    expect(fs.existsSync(path.join(TEST_DIR, 'cursor/.test/skills/skill1/SKILL.md'))).toBe(true);
+    expect(fs.existsSync(path.join(TEST_DIR, 'cursor/.test/skills/skill2/SKILL.md'))).toBe(true);
+  });
+
+  test('should preserve multiline body content', () => {
+    const transform = createTransformer(baseConfig);
+    const skills = [{
+      name: 'test',
+      description: 'Test',
+      body: `First paragraph.\n\nSecond paragraph.\n\n- List item 1\n- List item 2`
+    }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    const parsed = parseFrontmatter(content);
+    expect(parsed.body).toContain('First paragraph.');
+    expect(parsed.body).toContain('Second paragraph.');
+    expect(parsed.body).toContain('- List item 1');
+  });
+
+  test('should emit all spec fields when configured', () => {
+    const config = {
+      ...baseConfig,
+      frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata', 'allowed-tools'],
+    };
+    const transform = createTransformer(config);
+    const skills = [{
+      name: 'test',
+      description: 'Test',
+      userInvocable: true,
+      argumentHint: '[target]',
+      license: 'MIT',
+      compatibility: 'claude-code',
+      metadata: 'v1',
+      allowedTools: 'Bash,Edit',
+      body: 'Body'
+    }];
+    transform(skills, TEST_DIR);
+
+    const content = fs.readFileSync(path.join(TEST_DIR, 'cursor/.test/skills/test/SKILL.md'), 'utf-8');
+    expect(content).toContain('user-invocable: true');
+    expect(content).toContain('argument-hint:');
+    expect(content).toContain('license: MIT');
+    expect(content).toContain('compatibility: claude-code');
+    expect(content).toContain('metadata: v1');
+    expect(content).toContain('allowed-tools: Bash,Edit');
+  });
+});

tests/lib/transformers/providers.test.js 🔗

@@ -0,0 +1,192 @@
+import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
+import fs from 'fs';
+import path from 'path';
+import { PROVIDERS } from '../../../scripts/lib/transformers/providers.js';
+import { createTransformer } from '../../../scripts/lib/transformers/factory.js';
+import { parseFrontmatter, PROVIDER_PLACEHOLDERS } from '../../../scripts/lib/utils.js';
+
+const TEST_DIR = path.join(process.cwd(), 'test-tmp-providers');
+
+function providerTestDir(provider, suffix = '') {
+  return path.join(TEST_DIR, `${provider}${suffix}`);
+}
+
+function skillPath(config, skillName, suffix = '') {
+  return path.join(TEST_DIR, `${config.provider}${suffix}/${config.configDir}/skills/${skillName}/SKILL.md`);
+}
+
+// Test every provider config
+for (const [key, config] of Object.entries(PROVIDERS)) {
+  describe(`Provider: ${config.displayName} (${key})`, () => {
+    const transform = createTransformer(config);
+
+    beforeEach(() => {
+      if (fs.existsSync(TEST_DIR)) {
+        fs.rmSync(TEST_DIR, { recursive: true, force: true });
+      }
+    });
+
+    afterEach(() => {
+      if (fs.existsSync(TEST_DIR)) {
+        fs.rmSync(TEST_DIR, { recursive: true, force: true });
+      }
+    });
+
+    test('should create correct directory structure', () => {
+      transform([], TEST_DIR);
+      expect(fs.existsSync(path.join(TEST_DIR, `${config.provider}/${config.configDir}/skills`))).toBe(true);
+    });
+
+    test('should replace {{model}} placeholder correctly', () => {
+      const skills = [{ name: 'test', description: 'Test', body: 'Ask {{model}} for help.' }];
+      transform(skills, TEST_DIR);
+      const content = fs.readFileSync(skillPath(config, 'test'), 'utf-8');
+      const expected = PROVIDER_PLACEHOLDERS[config.placeholderProvider || config.provider].model;
+      expect(content).toContain(`Ask ${expected} for help.`);
+    });
+
+    test('should replace {{config_file}} placeholder correctly', () => {
+      const skills = [{ name: 'test', description: 'Test', body: 'See {{config_file}}.' }];
+      transform(skills, TEST_DIR);
+      const content = fs.readFileSync(skillPath(config, 'test'), 'utf-8');
+      const expected = PROVIDER_PLACEHOLDERS[config.placeholderProvider || config.provider].config_file;
+      expect(content).toContain(`See ${expected}.`);
+    });
+
+    test('should support prefix option', () => {
+      const skills = [{ name: 'audit', description: 'Audit', userInvocable: true, body: 'Body' }];
+      transform(skills, TEST_DIR, { prefix: 'i-', outputSuffix: '-prefixed' });
+      expect(fs.existsSync(skillPath(config, 'i-audit', '-prefixed'))).toBe(true);
+      const content = fs.readFileSync(skillPath(config, 'i-audit', '-prefixed'), 'utf-8');
+      expect(content).toContain('name: i-audit');
+    });
+
+    test('should copy reference files', () => {
+      const skills = [{
+        name: 'test',
+        description: 'Test',
+        body: 'Body',
+        references: [{ name: 'ref', content: 'Ref content', filePath: '/fake/ref.md' }]
+      }];
+      transform(skills, TEST_DIR);
+      const refPath = path.join(TEST_DIR, `${config.provider}/${config.configDir}/skills/test/reference/ref.md`);
+      expect(fs.existsSync(refPath)).toBe(true);
+    });
+
+    // Field-specific tests based on provider config
+    if (config.frontmatterFields.includes('user-invocable')) {
+      test('should emit user-invocable for user-invocable skills', () => {
+        const skills = [{ name: 'test', description: 'Test', userInvocable: true, body: 'Body' }];
+        transform(skills, TEST_DIR);
+        const parsed = parseFrontmatter(fs.readFileSync(skillPath(config, 'test'), 'utf-8'));
+        expect(parsed.frontmatter['user-invocable']).toBe(true);
+      });
+
+      test('should omit user-invocable for non-user-invocable skills', () => {
+        const skills = [{ name: 'test', description: 'Test', userInvocable: false, body: 'Body' }];
+        transform(skills, TEST_DIR);
+        const parsed = parseFrontmatter(fs.readFileSync(skillPath(config, 'test'), 'utf-8'));
+        expect(parsed.frontmatter['user-invocable']).toBeUndefined();
+      });
+    }
+
+    if (config.frontmatterFields.includes('argument-hint')) {
+      test('should emit argument-hint for user-invocable skills', () => {
+        const skills = [{ name: 'test', description: 'Test', userInvocable: true, argumentHint: '[target]', body: 'Body' }];
+        transform(skills, TEST_DIR);
+        const parsed = parseFrontmatter(fs.readFileSync(skillPath(config, 'test'), 'utf-8'));
+        expect(parsed.frontmatter['argument-hint']).toBe('[target]');
+      });
+
+      test('should not emit argument-hint for non-user-invocable skills', () => {
+        const skills = [{ name: 'test', description: 'Test', userInvocable: false, argumentHint: '[target]', body: 'Body' }];
+        transform(skills, TEST_DIR);
+        const parsed = parseFrontmatter(fs.readFileSync(skillPath(config, 'test'), 'utf-8'));
+        expect(parsed.frontmatter['argument-hint']).toBeUndefined();
+      });
+    }
+
+    if (config.frontmatterFields.includes('license')) {
+      test('should emit license when present', () => {
+        const skills = [{ name: 'test', description: 'Test', license: 'MIT', body: 'Body' }];
+        transform(skills, TEST_DIR);
+        const parsed = parseFrontmatter(fs.readFileSync(skillPath(config, 'test'), 'utf-8'));
+        expect(parsed.frontmatter.license).toBe('MIT');
+      });
+
+      test('should omit license when empty', () => {
+        const skills = [{ name: 'test', description: 'Test', license: '', body: 'Body' }];
+        transform(skills, TEST_DIR);
+        const parsed = parseFrontmatter(fs.readFileSync(skillPath(config, 'test'), 'utf-8'));
+        expect(parsed.frontmatter.license).toBeUndefined();
+      });
+    }
+
+    if (config.frontmatterFields.includes('compatibility')) {
+      test('should emit compatibility when present', () => {
+        const skills = [{ name: 'test', description: 'Test', compatibility: config.provider, body: 'Body' }];
+        transform(skills, TEST_DIR);
+        const content = fs.readFileSync(skillPath(config, 'test'), 'utf-8');
+        expect(content).toContain(`compatibility: ${config.provider}`);
+      });
+    }
+
+    if (config.frontmatterFields.includes('metadata')) {
+      test('should emit metadata when present', () => {
+        const skills = [{ name: 'test', description: 'Test', metadata: 'some-metadata', body: 'Body' }];
+        transform(skills, TEST_DIR);
+        const content = fs.readFileSync(skillPath(config, 'test'), 'utf-8');
+        expect(content).toContain('metadata: some-metadata');
+      });
+    }
+
+    if (config.frontmatterFields.includes('allowed-tools')) {
+      test('should emit allowed-tools when present', () => {
+        const skills = [{ name: 'test', description: 'Test', allowedTools: 'Bash,Edit', body: 'Body' }];
+        transform(skills, TEST_DIR);
+        const content = fs.readFileSync(skillPath(config, 'test'), 'utf-8');
+        expect(content).toContain('allowed-tools: Bash,Edit');
+      });
+    }
+
+    // Fields NOT in this provider's allowlist should not appear
+    const allOptionalFields = ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata', 'allowed-tools'];
+    const excludedFields = allOptionalFields.filter(f => !config.frontmatterFields.includes(f));
+
+    if (excludedFields.length > 0) {
+      test('should not emit fields outside allowlist', () => {
+        const skills = [{
+          name: 'test',
+          description: 'Test',
+          userInvocable: true,
+          argumentHint: '[target]',
+          license: 'MIT',
+          compatibility: 'all',
+          metadata: 'meta',
+          allowedTools: 'Bash',
+          body: 'Body'
+        }];
+        transform(skills, TEST_DIR);
+        const content = fs.readFileSync(skillPath(config, 'test'), 'utf-8');
+
+        for (const field of excludedFields) {
+          const yamlKey = field; // field names match yaml keys
+          // Check the raw content doesn't contain the yaml key
+          const lines = content.split('\n');
+          const frontmatterLines = [];
+          let inFrontmatter = false;
+          for (const line of lines) {
+            if (line === '---') {
+              if (inFrontmatter) break;
+              inFrontmatter = true;
+              continue;
+            }
+            if (inFrontmatter) frontmatterLines.push(line);
+          }
+          const hasForbiddenField = frontmatterLines.some(l => l.startsWith(`${yamlKey}:`));
+          expect(hasForbiddenField).toBe(false);
+        }
+      });
+    }
+  });
+}

tests/lib/utils.test.js 🔗

@@ -0,0 +1,708 @@
+import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
+import fs from 'fs';
+import path from 'path';
+import {
+  parseFrontmatter,
+  readFilesRecursive,
+  readSourceFiles,
+  ensureDir,
+  cleanDir,
+  writeFile,
+  generateYamlFrontmatter,
+  readPatterns,
+  replacePlaceholders,
+  prefixSkillReferences
+} from '../../scripts/lib/utils.js';
+
+// Temporary test directory
+const TEST_DIR = path.join(process.cwd(), 'test-tmp');
+
+describe('parseFrontmatter', () => {
+  test('should parse basic frontmatter with simple key-value pairs', () => {
+    const content = `---
+name: test-skill
+description: A test skill
+---
+
+This is the body content.`;
+
+    const result = parseFrontmatter(content);
+    expect(result.frontmatter.name).toBe('test-skill');
+    expect(result.frontmatter.description).toBe('A test skill');
+    expect(result.body).toBe('This is the body content.');
+  });
+
+  test('should parse frontmatter with argument-hint', () => {
+    const content = `---
+name: test-skill
+description: A test skill
+argument-hint: <output> [TARGET=<value>]
+---
+
+Body here.`;
+
+    const result = parseFrontmatter(content);
+    expect(result.frontmatter.name).toBe('test-skill');
+    expect(result.frontmatter['argument-hint']).toBe('<output> [TARGET=<value>]');
+  });
+
+  test('should return empty frontmatter when no frontmatter present', () => {
+    const content = 'Just some content without frontmatter.';
+    const result = parseFrontmatter(content);
+
+    expect(result.frontmatter).toEqual({});
+    expect(result.body).toBe(content);
+  });
+
+  test('should handle empty body', () => {
+    const content = `---
+name: test
+---
+`;
+    const result = parseFrontmatter(content);
+
+    expect(result.frontmatter.name).toBe('test');
+    expect(result.body).toBe('');
+  });
+
+  test('should handle frontmatter with license field', () => {
+    const content = `---
+name: skill-name
+description: A skill
+license: MIT
+---
+
+Skill body.`;
+
+    const result = parseFrontmatter(content);
+    expect(result.frontmatter.license).toBe('MIT');
+  });
+
+  test('should parse user-invocable boolean', () => {
+    const content = `---
+name: test-skill
+user-invocable: true
+---
+
+Body.`;
+
+    const result = parseFrontmatter(content);
+    expect(result.frontmatter['user-invocable']).toBe(true);
+  });
+
+  test('should parse user-invocable as string true (code behavior)', () => {
+    const content = `---
+name: test-skill
+user-invocable: 'true'
+---
+
+Body.`;
+
+    const result = parseFrontmatter(content);
+    // parseFrontmatter strips YAML quotes, so 'true' becomes boolean true
+    expect(result.frontmatter['user-invocable']).toBe(true);
+  });
+
+  test('should parse allowed-tools field', () => {
+    const content = `---
+name: test-skill
+allowed-tools: Bash
+---
+
+Body.`;
+
+    const result = parseFrontmatter(content);
+    expect(result.frontmatter['allowed-tools']).toBe('Bash');
+  });
+});
+
+describe('generateYamlFrontmatter', () => {
+  test('should generate basic frontmatter', () => {
+    const data = {
+      name: 'test-skill',
+      description: 'A test'
+    };
+
+    const result = generateYamlFrontmatter(data);
+    expect(result).toContain('---');
+    expect(result).toContain('name: test-skill');
+    expect(result).toContain('description: A test');
+  });
+
+  test('should generate frontmatter with argument-hint', () => {
+    const data = {
+      name: 'test',
+      description: 'Test skill',
+      'argument-hint': '<output> [TARGET=<value>]'
+    };
+
+    const result = generateYamlFrontmatter(data);
+    expect(result).toContain('argument-hint: <output> [TARGET=<value>]');
+  });
+
+  test('should generate frontmatter with boolean', () => {
+    const data = {
+      name: 'test',
+      description: 'Test',
+      'user-invocable': true
+    };
+
+    const result = generateYamlFrontmatter(data);
+    expect(result).toContain('user-invocable: true');
+  });
+
+  test('should roundtrip: generate and parse back', () => {
+    const original = {
+      name: 'roundtrip-test',
+      description: 'Testing roundtrip',
+      'argument-hint': '<arg1>'
+    };
+
+    const yaml = generateYamlFrontmatter(original);
+    const content = `${yaml}\n\nBody content`;
+    const parsed = parseFrontmatter(content);
+
+    expect(parsed.frontmatter.name).toBe(original.name);
+    expect(parsed.frontmatter.description).toBe(original.description);
+    expect(parsed.frontmatter['argument-hint']).toBe('<arg1>');
+  });
+});
+
+describe('ensureDir', () => {
+  afterEach(() => {
+    if (fs.existsSync(TEST_DIR)) {
+      fs.rmSync(TEST_DIR, { recursive: true, force: true });
+    }
+  });
+
+  test('should create directory if it does not exist', () => {
+    const testPath = path.join(TEST_DIR, 'new-dir');
+    ensureDir(testPath);
+
+    expect(fs.existsSync(testPath)).toBe(true);
+    expect(fs.statSync(testPath).isDirectory()).toBe(true);
+  });
+
+  test('should create nested directories', () => {
+    const testPath = path.join(TEST_DIR, 'level1', 'level2', 'level3');
+    ensureDir(testPath);
+
+    expect(fs.existsSync(testPath)).toBe(true);
+  });
+
+  test('should not throw if directory already exists', () => {
+    const testPath = path.join(TEST_DIR, 'existing');
+    fs.mkdirSync(testPath, { recursive: true });
+
+    expect(() => ensureDir(testPath)).not.toThrow();
+  });
+});
+
+describe('cleanDir', () => {
+  beforeEach(() => {
+    ensureDir(TEST_DIR);
+  });
+
+  afterEach(() => {
+    if (fs.existsSync(TEST_DIR)) {
+      fs.rmSync(TEST_DIR, { recursive: true, force: true });
+    }
+  });
+
+  test('should remove directory and all contents', () => {
+    const filePath = path.join(TEST_DIR, 'test.txt');
+    fs.writeFileSync(filePath, 'content');
+
+    expect(fs.existsSync(filePath)).toBe(true);
+
+    cleanDir(TEST_DIR);
+    expect(fs.existsSync(TEST_DIR)).toBe(false);
+  });
+
+  test('should not throw if directory does not exist', () => {
+    const nonExistent = path.join(TEST_DIR, 'does-not-exist');
+    expect(() => cleanDir(nonExistent)).not.toThrow();
+  });
+
+  test('should remove nested directories', () => {
+    const nestedPath = path.join(TEST_DIR, 'level1', 'level2');
+    ensureDir(nestedPath);
+    fs.writeFileSync(path.join(nestedPath, 'file.txt'), 'content');
+
+    cleanDir(TEST_DIR);
+    expect(fs.existsSync(TEST_DIR)).toBe(false);
+  });
+});
+
+describe('writeFile', () => {
+  afterEach(() => {
+    if (fs.existsSync(TEST_DIR)) {
+      fs.rmSync(TEST_DIR, { recursive: true, force: true });
+    }
+  });
+
+  test('should write file with content', () => {
+    const filePath = path.join(TEST_DIR, 'test.txt');
+    const content = 'Hello, world!';
+
+    writeFile(filePath, content);
+
+    expect(fs.existsSync(filePath)).toBe(true);
+    expect(fs.readFileSync(filePath, 'utf-8')).toBe(content);
+  });
+
+  test('should create parent directories automatically', () => {
+    const filePath = path.join(TEST_DIR, 'nested', 'deep', 'file.txt');
+    writeFile(filePath, 'content');
+
+    expect(fs.existsSync(filePath)).toBe(true);
+    expect(fs.readFileSync(filePath, 'utf-8')).toBe('content');
+  });
+
+  test('should overwrite existing file', () => {
+    const filePath = path.join(TEST_DIR, 'file.txt');
+    writeFile(filePath, 'first');
+    writeFile(filePath, 'second');
+
+    expect(fs.readFileSync(filePath, 'utf-8')).toBe('second');
+  });
+});
+
+describe('readFilesRecursive', () => {
+  beforeEach(() => {
+    ensureDir(TEST_DIR);
+  });
+
+  afterEach(() => {
+    if (fs.existsSync(TEST_DIR)) {
+      fs.rmSync(TEST_DIR, { recursive: true, force: true });
+    }
+  });
+
+  test('should find all markdown files in directory', () => {
+    writeFile(path.join(TEST_DIR, 'file1.md'), 'content1');
+    writeFile(path.join(TEST_DIR, 'file2.md'), 'content2');
+    writeFile(path.join(TEST_DIR, 'file3.txt'), 'not markdown');
+
+    const files = readFilesRecursive(TEST_DIR);
+    expect(files).toHaveLength(2);
+    expect(files.some(f => f.endsWith('file1.md'))).toBe(true);
+    expect(files.some(f => f.endsWith('file2.md'))).toBe(true);
+  });
+
+  test('should find markdown files in nested directories', () => {
+    writeFile(path.join(TEST_DIR, 'root.md'), 'root');
+    writeFile(path.join(TEST_DIR, 'sub', 'nested.md'), 'nested');
+    writeFile(path.join(TEST_DIR, 'sub', 'deep', 'deeper.md'), 'deeper');
+
+    const files = readFilesRecursive(TEST_DIR);
+    expect(files).toHaveLength(3);
+    expect(files.some(f => f.endsWith('root.md'))).toBe(true);
+    expect(files.some(f => f.endsWith('nested.md'))).toBe(true);
+    expect(files.some(f => f.endsWith('deeper.md'))).toBe(true);
+  });
+
+  test('should return empty array for non-existent directory', () => {
+    const files = readFilesRecursive(path.join(TEST_DIR, 'does-not-exist'));
+    expect(files).toEqual([]);
+  });
+
+  test('should return empty array for directory with no markdown files', () => {
+    writeFile(path.join(TEST_DIR, 'file.txt'), 'text');
+    writeFile(path.join(TEST_DIR, 'file.js'), 'code');
+
+    const files = readFilesRecursive(TEST_DIR);
+    expect(files).toEqual([]);
+  });
+});
+
+describe('readSourceFiles', () => {
+  const testRootDir = TEST_DIR;
+
+  beforeEach(() => {
+    ensureDir(testRootDir);
+  });
+
+  afterEach(() => {
+    if (fs.existsSync(testRootDir)) {
+      fs.rmSync(testRootDir, { recursive: true, force: true });
+    }
+  });
+
+  test('should read and parse skill files from directory-based structure', () => {
+    const skillContent = `---
+name: test-skill
+description: A test skill
+license: MIT
+---
+
+Skill instructions here.`;
+
+    const skillDir = path.join(testRootDir, 'source/skills/test-skill');
+    ensureDir(skillDir);
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
+
+    const { skills } = readSourceFiles(testRootDir);
+
+    expect(skills).toHaveLength(1);
+    expect(skills[0].name).toBe('test-skill');
+    expect(skills[0].description).toBe('A test skill');
+    expect(skills[0].license).toBe('MIT');
+    expect(skills[0].body).toBe('Skill instructions here.');
+  });
+
+  test('should read skill with user-invocable flag', () => {
+    const skillContent = `---
+name: audit
+description: Run technical quality checks
+user-invocable: true
+---
+
+Audit the code.`;
+
+    const skillDir = path.join(testRootDir, 'source/skills/audit');
+    ensureDir(skillDir);
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
+
+    const { skills } = readSourceFiles(testRootDir);
+
+    expect(skills).toHaveLength(1);
+    expect(skills[0].userInvocable).toBe(true);
+  });
+
+  test('should read skill with reference files', () => {
+    const skillContent = `---
+name: impeccable
+description: Impeccable design skill
+---
+
+Impeccable design instructions.`;
+
+    const skillDir = path.join(testRootDir, 'source/skills/impeccable');
+    ensureDir(skillDir);
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
+
+    const refDir = path.join(skillDir, 'reference');
+    ensureDir(refDir);
+    fs.writeFileSync(path.join(refDir, 'typography.md'), 'Typography reference content.');
+    fs.writeFileSync(path.join(refDir, 'color.md'), 'Color reference content.');
+
+    const { skills } = readSourceFiles(testRootDir);
+
+    expect(skills).toHaveLength(1);
+    expect(skills[0].references).toHaveLength(2);
+    // References may not be in a specific order due to fs.readdirSync
+    const refNames = skills[0].references.map(r => r.name).sort();
+    expect(refNames).toEqual(['color', 'typography']);
+  });
+
+  test('should use filename as name if not in frontmatter', () => {
+    const skillDir = path.join(testRootDir, 'source/skills/my-skill');
+    ensureDir(skillDir);
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), 'Just body, no frontmatter.');
+
+    const { skills } = readSourceFiles(testRootDir);
+
+    expect(skills).toHaveLength(1);
+    expect(skills[0].name).toBe('my-skill');
+  });
+
+  test('should handle empty source directories', () => {
+    ensureDir(path.join(testRootDir, 'source/skills'));
+
+    const { skills } = readSourceFiles(testRootDir);
+
+    expect(skills).toEqual([]);
+  });
+
+  test('should read multiple skills', () => {
+    const skill1Dir = path.join(testRootDir, 'source/skills/skill1');
+    ensureDir(skill1Dir);
+    fs.writeFileSync(path.join(skill1Dir, 'SKILL.md'), '---\nname: skill1\n---\nSkill1');
+
+    const skill2Dir = path.join(testRootDir, 'source/skills/skill2');
+    ensureDir(skill2Dir);
+    fs.writeFileSync(path.join(skill2Dir, 'SKILL.md'), '---\nname: skill2\n---\nSkill2');
+
+    const { skills } = readSourceFiles(testRootDir);
+
+    expect(skills).toHaveLength(2);
+  });
+
+  test('should ignore non-md files in skill directories', () => {
+    const skillDir = path.join(testRootDir, 'source/skills/test-skill');
+    ensureDir(skillDir);
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), '---\nname: test-skill\n---\nBody');
+
+    const refDir = path.join(skillDir, 'reference');
+    ensureDir(refDir);
+    fs.writeFileSync(path.join(refDir, 'readme.txt'), 'Not a markdown file');
+    fs.writeFileSync(path.join(refDir, 'typography.md'), 'Valid reference');
+
+    const { skills } = readSourceFiles(testRootDir);
+
+    expect(skills).toHaveLength(1);
+    expect(skills[0].references).toHaveLength(1);
+    expect(skills[0].references[0].name).toBe('typography');
+  });
+
+  test('should handle missing skills directory', () => {
+    const { skills } = readSourceFiles(testRootDir);
+    expect(skills).toEqual([]);
+  });
+
+  test('should parse all frontmatter fields correctly', () => {
+    const skillContent = `---
+name: test-skill
+description: A comprehensive test skill
+license: Apache-2.0
+compatibility: claude-code
+user-invocable: true
+allowed-tools: Bash,Edit
+---
+
+Body content.`;
+
+    const skillDir = path.join(testRootDir, 'source/skills/test-skill');
+    ensureDir(skillDir);
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
+
+    const { skills } = readSourceFiles(testRootDir);
+
+    expect(skills[0].name).toBe('test-skill');
+    expect(skills[0].description).toBe('A comprehensive test skill');
+    expect(skills[0].license).toBe('Apache-2.0');
+    expect(skills[0].compatibility).toBe('claude-code');
+    expect(skills[0].userInvocable).toBe(true);
+    expect(skills[0].allowedTools).toBe('Bash,Edit');
+  });
+});
+
+describe('readPatterns', () => {
+  const testRootDir = TEST_DIR;
+
+  beforeEach(() => {
+    ensureDir(testRootDir);
+  });
+
+  afterEach(() => {
+    if (fs.existsSync(testRootDir)) {
+      fs.rmSync(testRootDir, { recursive: true, force: true });
+    }
+  });
+
+  test('should extract DO and DON\'T patterns from SKILL.md', () => {
+    const skillContent = `---
+name: impeccable
+---
+
+### Typography
+**DO**: Use variable fonts for flexibility.
+**DON'T**: Use system fonts like Arial.
+
+### Color & Contrast
+**DO**: Ensure WCAG AA compliance.
+**DON'T**: Use gray text on colored backgrounds.
+
+### Layout & Space
+**DO**: Use consistent spacing scale.
+**DON'T**: Nest cards inside cards.`;
+
+    const skillDir = path.join(testRootDir, 'source/skills/impeccable');
+    ensureDir(skillDir);
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
+
+    const { patterns, antipatterns } = readPatterns(testRootDir);
+
+    expect(patterns).toHaveLength(3);
+    expect(antipatterns).toHaveLength(3);
+
+    expect(patterns[0].name).toBe('Typography');
+    expect(patterns[0].items).toContain('Use variable fonts for flexibility.');
+    expect(antipatterns[0].items).toContain('Use system fonts like Arial.');
+  });
+
+  test('should normalize "Color & Theme" to "Color & Contrast"', () => {
+    const skillContent = `---
+name: impeccable
+---
+
+### Color & Theme
+**DO**: Use OKLCH color space.
+**DON'T**: Use pure black.`;
+
+    const skillDir = path.join(testRootDir, 'source/skills/impeccable');
+    ensureDir(skillDir);
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
+
+    const { patterns, antipatterns } = readPatterns(testRootDir);
+
+    expect(patterns[0].name).toBe('Color & Contrast');
+  });
+
+  test('should handle missing SKILL.md file', () => {
+    ensureDir(path.join(testRootDir, 'source/skills/impeccable'));
+
+    const { patterns, antipatterns } = readPatterns(testRootDir);
+
+    expect(patterns).toEqual([]);
+    expect(antipatterns).toEqual([]);
+  });
+
+  test('should return patterns in consistent section order', () => {
+    const skillContent = `---
+name: impeccable
+---
+
+### Motion
+**DO**: Use ease-out for natural movement.
+
+### Typography
+**DO**: Use modular scale.
+
+### Color & Contrast
+**DO**: Use tinted neutrals.`;
+
+    const skillDir = path.join(testRootDir, 'source/skills/impeccable');
+    ensureDir(skillDir);
+    fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent);
+
+    const { patterns } = readPatterns(testRootDir);
+
+    // Patterns are returned in predefined section order, not source order
+    // Only sections with content are included
+    expect(patterns[0].name).toBe('Typography');
+    expect(patterns[1].name).toBe('Color & Contrast');
+    expect(patterns[2].name).toBe('Motion');
+    expect(patterns.length).toBe(3);
+  });
+});
+
+describe('replacePlaceholders', () => {
+  test('should replace {{model}} with provider-specific value', () => {
+    expect(replacePlaceholders('Ask {{model}} for help.', 'claude-code')).toBe('Ask Claude for help.');
+    expect(replacePlaceholders('Ask {{model}} for help.', 'gemini')).toBe('Ask Gemini for help.');
+    expect(replacePlaceholders('Ask {{model}} for help.', 'codex')).toBe('Ask GPT for help.');
+    expect(replacePlaceholders('Ask {{model}} for help.', 'cursor')).toBe('Ask the model for help.');
+    expect(replacePlaceholders('Ask {{model}} for help.', 'agents')).toBe('Ask the model for help.');
+    expect(replacePlaceholders('Ask {{model}} for help.', 'kiro')).toBe('Ask Claude for help.');
+  });
+
+  test('should replace {{config_file}} with provider-specific value', () => {
+    expect(replacePlaceholders('See {{config_file}}.', 'claude-code')).toBe('See CLAUDE.md.');
+    expect(replacePlaceholders('See {{config_file}}.', 'cursor')).toBe('See .cursorrules.');
+    expect(replacePlaceholders('See {{config_file}}.', 'gemini')).toBe('See GEMINI.md.');
+    expect(replacePlaceholders('See {{config_file}}.', 'codex')).toBe('See AGENTS.md.');
+    expect(replacePlaceholders('See {{config_file}}.', 'agents')).toBe('See .github/copilot-instructions.md.');
+    expect(replacePlaceholders('See {{config_file}}.', 'kiro')).toBe('See .kiro/settings.json.');
+  });
+
+  test('should replace {{ask_instruction}} with provider-specific value', () => {
+    const result = replacePlaceholders('{{ask_instruction}}', 'claude-code');
+    expect(result).toBe('STOP and call the AskUserQuestion tool to clarify.');
+
+    const cursorResult = replacePlaceholders('{{ask_instruction}}', 'cursor');
+    expect(cursorResult).toBe('ask the user directly to clarify what you cannot infer.');
+  });
+
+  test('should replace {{available_commands}} with command list', () => {
+    const result = replacePlaceholders('Commands: {{available_commands}}', 'claude-code', ['audit', 'polish', 'optimize']);
+    expect(result).toBe('Commands: /audit, /polish, /optimize');
+  });
+
+  test('should exclude impeccable from {{available_commands}}', () => {
+    const result = replacePlaceholders('Commands: {{available_commands}}', 'claude-code', ['audit', 'impeccable', 'polish']);
+    expect(result).toBe('Commands: /audit, /polish');
+  });
+
+  test('should exclude i-impeccable from {{available_commands}}', () => {
+    const result = replacePlaceholders('Commands: {{available_commands}}', 'claude-code', ['i-audit', 'i-impeccable', 'i-polish']);
+    expect(result).toBe('Commands: /i-audit, /i-polish');
+  });
+
+  test('should exclude legacy teach-impeccable from {{available_commands}}', () => {
+    const result = replacePlaceholders('Commands: {{available_commands}}', 'claude-code', ['audit', 'teach-impeccable', 'polish']);
+    expect(result).toBe('Commands: /audit, /polish');
+  });
+
+  test('should exclude legacy i-teach-impeccable from {{available_commands}}', () => {
+    const result = replacePlaceholders('Commands: {{available_commands}}', 'claude-code', ['i-audit', 'i-teach-impeccable', 'i-polish']);
+    expect(result).toBe('Commands: /i-audit, /i-polish');
+  });
+
+  test('should produce empty string for {{available_commands}} with no commands', () => {
+    const result = replacePlaceholders('Commands: {{available_commands}}.', 'claude-code', []);
+    expect(result).toBe('Commands: .');
+  });
+
+  test('should replace multiple placeholders in the same string', () => {
+    const result = replacePlaceholders('{{model}} uses {{config_file}} and {{ask_instruction}}', 'claude-code');
+    expect(result).toBe('Claude uses CLAUDE.md and STOP and call the AskUserQuestion tool to clarify.');
+  });
+
+  test('should replace multiple occurrences of the same placeholder', () => {
+    const result = replacePlaceholders('{{model}} and {{model}} again.', 'gemini');
+    expect(result).toBe('Gemini and Gemini again.');
+  });
+
+  test('should fall back to cursor placeholders for unknown provider', () => {
+    const result = replacePlaceholders('{{model}} {{config_file}}', 'unknown-provider');
+    expect(result).toBe('the model .cursorrules');
+  });
+});
+
+describe('prefixSkillReferences', () => {
+  test('should prefix /skillname command references', () => {
+    const result = prefixSkillReferences('Run /audit to check.', 'i-', ['audit', 'polish']);
+    expect(result).toBe('Run /i-audit to check.');
+  });
+
+  test('should prefix "the skillname skill" references', () => {
+    const result = prefixSkillReferences('Use the audit skill for checks.', 'i-', ['audit', 'polish']);
+    expect(result).toBe('Use the i-audit skill for checks.');
+  });
+
+  test('should prefix multiple different references', () => {
+    const result = prefixSkillReferences('Run /audit then /polish. The audit skill is great.', 'i-', ['audit', 'polish']);
+    expect(result).toContain('/i-audit');
+    expect(result).toContain('/i-polish');
+    expect(result).toContain('The i-audit skill');
+  });
+
+  test('should not partially match longer skill names', () => {
+    const result = prefixSkillReferences('Run /teach-impeccable command.', 'i-', ['teach', 'teach-impeccable']);
+    expect(result).toBe('Run /i-teach-impeccable command.');
+  });
+
+  test('should handle case-insensitive "the X skill" matching', () => {
+    const result = prefixSkillReferences('The audit skill is useful.', 'i-', ['audit']);
+    expect(result).toBe('The i-audit skill is useful.');
+  });
+
+  test('should return content unchanged with empty prefix', () => {
+    const result = prefixSkillReferences('Run /audit.', '', ['audit']);
+    expect(result).toBe('Run /audit.');
+  });
+
+  test('should return content unchanged with empty skill names', () => {
+    const result = prefixSkillReferences('Run /audit.', 'i-', []);
+    expect(result).toBe('Run /audit.');
+  });
+
+  test('should not match /skillname inside longer words', () => {
+    const result = prefixSkillReferences('The /auditing process.', 'i-', ['audit']);
+    // 'auditing' starts with 'audit' but has trailing letters — should NOT match
+    expect(result).toBe('The /auditing process.');
+  });
+
+  test('should match /skillname at end of string', () => {
+    const result = prefixSkillReferences('Run /audit', 'i-', ['audit']);
+    expect(result).toBe('Run /i-audit');
+  });
+
+  test('should match /skillname before punctuation', () => {
+    const result = prefixSkillReferences('Try /audit, /polish.', 'i-', ['audit', 'polish']);
+    expect(result).toContain('/i-audit,');
+    expect(result).toContain('/i-polish.');
+  });
+});

tests/server/download-validation.test.js 🔗

@@ -0,0 +1,46 @@
+import { describe, expect, test } from 'bun:test';
+import path from 'path';
+import {
+  ALLOWED_BUNDLE_PROVIDERS,
+  ALLOWED_FILE_PROVIDERS,
+  isAllowedBundleProvider,
+  isAllowedFileProvider,
+  isAllowedProvider,
+} from '../../server/lib/validation.js';
+import { getFilePath, handleFileDownload } from '../../server/lib/api-handlers.js';
+
+describe('download provider validation', () => {
+  test('allows opencode and pi as individual download providers', () => {
+    expect(ALLOWED_FILE_PROVIDERS).toContain('opencode');
+    expect(ALLOWED_FILE_PROVIDERS).toContain('pi');
+    expect(isAllowedFileProvider('opencode')).toBe(true);
+    expect(isAllowedFileProvider('pi')).toBe(true);
+  });
+
+  test('separates file downloads from bundle downloads', () => {
+    expect(ALLOWED_BUNDLE_PROVIDERS).toContain('universal');
+    expect(ALLOWED_BUNDLE_PROVIDERS).toContain('universal-prefixed');
+    expect(isAllowedBundleProvider('universal')).toBe(true);
+    expect(isAllowedProvider('universal')).toBe(true);
+    expect(isAllowedFileProvider('universal')).toBe(false);
+  });
+});
+
+describe('download file paths', () => {
+  test('maps opencode skills into the .opencode config directory', () => {
+    expect(getFilePath('skill', 'opencode', 'impeccable')).toBe(
+      path.join(process.cwd(), 'dist', 'opencode', '.opencode', 'skills', 'impeccable', 'SKILL.md')
+    );
+  });
+
+  test('maps pi commands into the .pi config directory', () => {
+    expect(getFilePath('command', 'pi', 'audit')).toBe(
+      path.join(process.cwd(), 'dist', 'pi', '.pi', 'skills', 'audit', 'SKILL.md')
+    );
+  });
+
+  test('rejects bundle-only providers on the individual download route', async () => {
+    const response = await handleFileDownload('skill', 'universal', 'impeccable');
+    expect(response.status).toBe(400);
+  });
+});

tests/skills-cli.test.js 🔗

@@ -0,0 +1,417 @@
+/**
+ * End-to-end tests for `impeccable skills` subcommands.
+ *
+ * Creates real temp directories, runs the CLI, and verifies results.
+ * Tests that require `npx skills` are skipped if it's not available.
+ */
+import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
+import { execSync } from 'child_process';
+import { mkdtempSync, existsSync, readdirSync, readFileSync, mkdirSync, writeFileSync, rmSync } from 'fs';
+import { join } from 'path';
+import { tmpdir } from 'os';
+
+const CLI = join(import.meta.dir, '..', 'bin', 'cli.js');
+
+function run(args, opts = {}) {
+  return execSync(`node ${CLI} ${args}`, {
+    encoding: 'utf8',
+    timeout: 60000,
+    ...opts,
+  });
+}
+
+/** Create a fake skill installation in a temp dir */
+function createFakeSkills(root, skills = ['audit', 'polish', 'impeccable'], providers = ['.claude']) {
+  for (const provider of providers) {
+    for (const skill of skills) {
+      const skillDir = join(root, provider, 'skills', skill);
+      mkdirSync(skillDir, { recursive: true });
+      writeFileSync(join(skillDir, 'SKILL.md'), [
+        '---',
+        `name: ${skill}`,
+        'user-invocable: true',
+        '---',
+        '',
+        'Run /audit first, then /polish to finish.',
+        'Use the impeccable skill for setup.',
+      ].join('\n'));
+    }
+  }
+}
+
+// ─── Already-installed detection ─────────────────────────────────────────────
+
+describe('skills install: already-installed detection', () => {
+  test('detects impeccable sentinel and bails', () => {
+    const tmp = mkdtempSync(join(tmpdir(), 'imp-test-'));
+    execSync('git init', { cwd: tmp });
+    createFakeSkills(tmp);
+
+    const output = run('skills install -y', { cwd: tmp });
+    expect(output).toContain('already installed');
+
+    rmSync(tmp, { recursive: true, force: true });
+  }, 15000);
+
+  test('detects prefixed i-impeccable', () => {
+    const tmp = mkdtempSync(join(tmpdir(), 'imp-test-'));
+    execSync('git init', { cwd: tmp });
+
+    const skillDir = join(tmp, '.cursor', 'skills', 'i-impeccable');
+    mkdirSync(skillDir, { recursive: true });
+    writeFileSync(join(skillDir, 'SKILL.md'), '---\nname: i-impeccable\n---\n');
+
+    const output = run('skills install -y', { cwd: tmp });
+    expect(output).toContain('already installed');
+
+    rmSync(tmp, { recursive: true, force: true });
+  }, 15000);
+});
+
+// ─── Prefix rename (real filesystem) ─────────────────────────────────────────
+
+describe('skills install: prefix rename', () => {
+  let tmp;
+
+  beforeAll(() => {
+    tmp = mkdtempSync(join(tmpdir(), 'imp-test-pfx-'));
+    createFakeSkills(tmp, ['audit', 'polish', 'impeccable'], ['.claude', '.cursor']);
+  });
+
+  afterAll(() => {
+    if (tmp) rmSync(tmp, { recursive: true, force: true });
+  });
+
+  test('renames folders with prefix', () => {
+    // Write a helper script that imports and runs renameSkillsWithPrefix
+    const helperScript = join(tmp, '_test_rename.mjs');
+    writeFileSync(helperScript, `
+import { existsSync, readdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
+import { join } from 'node:path';
+
+function escapeRegex(str) {
+  return str.replace(/[.*+?^$\{\}()|[\\]\\\\]/g, '\\\\$&');
+}
+
+function prefixSkillContent(content, prefix, allSkillNames) {
+  let result = content.replace(/^name:\\s*(.+)$/m, (_, name) => 'name: ' + prefix + name.trim());
+  const sorted = [...allSkillNames].sort((a, b) => b.length - a.length);
+  for (const name of sorted) {
+    result = result.replace(
+      new RegExp('/' + '(?=' + escapeRegex(name) + '(?:[^a-zA-Z0-9_-]|$))', 'g'),
+      '/' + prefix
+    );
+    result = result.replace(
+      new RegExp('(the) ' + escapeRegex(name) + ' skill', 'gi'),
+      (_, article) => article + ' ' + prefix + name + ' skill'
+    );
+  }
+  return result;
+}
+
+const DIRS = ['.claude', '.cursor'];
+const root = process.argv[2];
+const prefix = process.argv[3];
+
+let allNames = [];
+for (const d of DIRS) {
+  const dir = join(root, d, 'skills');
+  if (!existsSync(dir)) continue;
+  allNames = readdirSync(dir, { withFileTypes: true }).filter(e => e.isDirectory()).map(e => e.name);
+  if (allNames.length > 0) break;
+}
+
+let count = 0;
+for (const d of DIRS) {
+  const dir = join(root, d, 'skills');
+  if (!existsSync(dir)) continue;
+  for (const entry of readdirSync(dir, { withFileTypes: true })) {
+    if (!entry.isDirectory() || entry.name.startsWith(prefix)) continue;
+    const skillMd = join(dir, entry.name, 'SKILL.md');
+    if (!existsSync(skillMd)) continue;
+    renameSync(join(dir, entry.name), join(dir, prefix + entry.name));
+    let content = readFileSync(join(dir, prefix + entry.name, 'SKILL.md'), 'utf8');
+    content = prefixSkillContent(content, prefix, allNames);
+    writeFileSync(join(dir, prefix + entry.name, 'SKILL.md'), content);
+    count++;
+  }
+}
+console.log(JSON.stringify({ count }));
+    `);
+
+    const output = JSON.parse(execSync(`node ${helperScript} ${tmp} i-`, { encoding: 'utf8' }));
+    expect(output.count).toBe(6); // 3 skills x 2 providers
+
+    // Verify folders renamed
+    const skills = readdirSync(join(tmp, '.claude', 'skills'));
+    expect(skills).toContain('i-audit');
+    expect(skills).toContain('i-polish');
+    expect(skills).toContain('i-impeccable');
+    expect(skills).not.toContain('audit');
+    expect(skills).not.toContain('polish');
+  }, 15000);
+
+  test('prefixed SKILL.md has correct name and cross-references', () => {
+    const content = readFileSync(join(tmp, '.claude', 'skills', 'i-audit', 'SKILL.md'), 'utf8');
+    expect(content).toContain('name: i-audit');
+    expect(content).toContain('/i-audit');
+    expect(content).toContain('/i-polish');
+    expect(content).toContain('the i-impeccable skill');
+    // Original unprefixed references should be gone
+    expect(content).not.toMatch(/\/audit(?=[^a-zA-Z0-9_-]|$)/);
+  });
+
+  test('also prefixed in second provider', () => {
+    const skills = readdirSync(join(tmp, '.cursor', 'skills'));
+    expect(skills).toContain('i-audit');
+    expect(skills).toContain('i-impeccable');
+  });
+});
+
+// ─── Update fallback (direct download) ───────────────────────────────────────
+
+describe('skills update: direct download fallback', () => {
+  let tmp;
+
+  beforeAll(() => {
+    tmp = mkdtempSync(join(tmpdir(), 'imp-test-update-'));
+    execSync('git init', { cwd: tmp });
+
+    // Create stale skills that the update should overwrite
+    for (const skill of ['audit', 'impeccable']) {
+      const skillDir = join(tmp, '.claude', 'skills', skill);
+      mkdirSync(skillDir, { recursive: true });
+      writeFileSync(join(skillDir, 'SKILL.md'), `---\nname: ${skill}\nstale: true\n---\nOld content.\n`);
+    }
+  });
+
+  afterAll(() => {
+    if (tmp) rmSync(tmp, { recursive: true, force: true });
+  });
+
+  test('downloads universal bundle and updates skills', () => {
+    const output = run('skills update -y', { cwd: tmp });
+    expect(output).toContain('direct download');
+    expect(output).toContain('Updated');
+
+    // Skills should have fresh content (no 'stale: true')
+    const content = readFileSync(join(tmp, '.claude', 'skills', 'audit', 'SKILL.md'), 'utf8');
+    expect(content).not.toContain('stale: true');
+    expect(content).toContain('name:');
+  }, 60000);
+
+  test('update added new skills that were not present before', () => {
+    // The universal bundle has ~20 skills, we only had 2
+    const skills = readdirSync(join(tmp, '.claude', 'skills'));
+    expect(skills.length).toBeGreaterThan(5);
+  });
+});
+
+// ─── Prefix round-trip: detect, undo, re-apply ──────────────────────────────
+
+describe('prefix round-trip: detect, undo, re-apply', () => {
+  let tmp;
+
+  beforeAll(() => {
+    tmp = mkdtempSync(join(tmpdir(), 'imp-test-roundtrip-'));
+    // Create prefixed skills to simulate post-install state
+    for (const skill of ['audit', 'polish', 'impeccable']) {
+      const skillDir = join(tmp, '.claude', 'skills', 'i-' + skill);
+      mkdirSync(skillDir, { recursive: true });
+      writeFileSync(join(skillDir, 'SKILL.md'), [
+        '---',
+        `name: i-${skill}`,
+        'user-invocable: true',
+        '---',
+        '',
+        'Run /i-audit first, then /i-polish to finish.',
+        'Use the i-impeccable skill for setup.',
+      ].join('\n'));
+    }
+  });
+
+  afterAll(() => {
+    if (tmp) rmSync(tmp, { recursive: true, force: true });
+  });
+
+  test('detectPrefix finds the prefix from skill names', () => {
+    const script = join(tmp, '_detect.mjs');
+    writeFileSync(script, `
+import { existsSync, readdirSync } from 'node:fs';
+import { join } from 'node:path';
+const DIRS = ['.claude', '.cursor', '.agents'];
+const root = ${JSON.stringify(tmp)};
+for (const d of DIRS) {
+  const dir = join(root, d, 'skills');
+  if (!existsSync(dir)) continue;
+  for (const name of readdirSync(dir)) {
+    if (name === 'impeccable') { console.log(''); process.exit(); }
+    if (name.endsWith('-impeccable')) { console.log(name.slice(0, -'impeccable'.length)); process.exit(); }
+  }
+}
+console.log('');
+    `);
+    const output = execSync(`node ${script}`, { encoding: 'utf8' }).trim();
+    expect(output).toBe('i-');
+  });
+
+  test('undo removes prefix from folders and content', () => {
+    // Write undo helper script
+    const script = join(tmp, '_undo.mjs');
+    writeFileSync(script, `
+import { existsSync, readdirSync, readFileSync, lstatSync, readlinkSync, unlinkSync, renameSync, writeFileSync, symlinkSync } from 'node:fs';
+import { join } from 'node:path';
+
+function escapeRegex(str) { return str.replace(/[.*+?^$\{\\}()|[\\]\\\\]/g, '\\\\$&'); }
+
+const root = ${JSON.stringify(tmp)};
+const prefix = 'i-';
+const skillsDir = join(root, '.claude', 'skills');
+const entries = readdirSync(skillsDir);
+const prefixedNames = entries.filter(n => n.startsWith(prefix));
+
+for (const name of entries) {
+  if (!name.startsWith(prefix)) continue;
+  const unprefixed = name.slice(prefix.length);
+  const src = join(skillsDir, name);
+  const dest = join(skillsDir, unprefixed);
+
+  if (lstatSync(src).isSymbolicLink()) {
+    const target = readlinkSync(src);
+    unlinkSync(src);
+    symlinkSync(target.replace('/' + name, '/' + unprefixed), dest);
+  } else {
+    renameSync(src, dest);
+    const skillMd = join(dest, 'SKILL.md');
+    if (existsSync(skillMd)) {
+      let content = readFileSync(skillMd, 'utf8');
+      content = content.replace(new RegExp('^name:\\\\s*' + escapeRegex(prefix), 'm'), 'name: ');
+      const sorted = [...prefixedNames].sort((a, b) => b.length - a.length);
+      for (const pName of sorted) {
+        const uName = pName.slice(prefix.length);
+        content = content.replace(new RegExp('/' + escapeRegex(pName) + '(?=[^a-zA-Z0-9_-]|$)', 'g'), '/' + uName);
+        content = content.replace(new RegExp('(the) ' + escapeRegex(pName) + ' skill', 'gi'), '$1 ' + uName + ' skill');
+      }
+      writeFileSync(skillMd, content);
+    }
+  }
+}
+console.log(JSON.stringify(readdirSync(skillsDir)));
+    `);
+    const output = JSON.parse(execSync(`node ${script}`, { encoding: 'utf8' }));
+    expect(output).toContain('audit');
+    expect(output).toContain('polish');
+    expect(output).toContain('impeccable');
+    expect(output).not.toContain('i-audit');
+
+    // Verify content was un-prefixed
+    const content = readFileSync(join(tmp, '.claude', 'skills', 'audit', 'SKILL.md'), 'utf8');
+    expect(content).toContain('name: audit');
+    expect(content).toContain('/audit');
+    expect(content).toContain('/polish');
+    expect(content).toContain('the impeccable skill');
+    expect(content).not.toContain('/i-audit');
+    expect(content).not.toContain('i-impeccable');
+  });
+
+  test('re-applying prefix restores original state', () => {
+    // Now re-apply using the same helper from the rename test
+    const script = join(tmp, '_reprefix.mjs');
+    writeFileSync(script, `
+import { existsSync, readdirSync, readFileSync, statSync, lstatSync, renameSync, writeFileSync } from 'node:fs';
+import { join } from 'node:path';
+
+function escapeRegex(str) { return str.replace(/[.*+?^$\{\\}()|[\\]\\\\]/g, '\\\\$&'); }
+
+const root = ${JSON.stringify(tmp)};
+const prefix = 'i-';
+const skillsDir = join(root, '.claude', 'skills');
+const allNames = readdirSync(skillsDir).filter(n => {
+  const full = join(skillsDir, n);
+  try { return statSync(full).isDirectory() && existsSync(join(full, 'SKILL.md')); } catch { return false; }
+});
+
+for (const name of allNames) {
+  if (name.startsWith(prefix)) continue;
+  const src = join(skillsDir, name);
+  const dest = join(skillsDir, prefix + name);
+  const ls = lstatSync(src);
+  if (ls.isSymbolicLink() || !ls.isDirectory()) continue;
+  renameSync(src, dest);
+  let content = readFileSync(join(dest, 'SKILL.md'), 'utf8');
+  content = content.replace(/^name:\\s*(.+)$/m, (_, n) => 'name: ' + prefix + n.trim());
+  const sorted = [...allNames].sort((a, b) => b.length - a.length);
+  for (const n of sorted) {
+    content = content.replace(new RegExp('/' + '(?=' + escapeRegex(n) + '(?:[^a-zA-Z0-9_-]|$))', 'g'), '/' + prefix);
+    content = content.replace(new RegExp('(the) ' + escapeRegex(n) + ' skill', 'gi'), (_, art) => art + ' ' + prefix + n + ' skill');
+  }
+  writeFileSync(join(dest, 'SKILL.md'), content);
+}
+console.log(JSON.stringify(readdirSync(skillsDir)));
+    `);
+    const output = JSON.parse(execSync(`node ${script}`, { encoding: 'utf8' }));
+    expect(output).toContain('i-audit');
+    expect(output).toContain('i-polish');
+    expect(output).toContain('i-impeccable');
+    expect(output).not.toContain('audit');
+
+    // Verify content was re-prefixed
+    const content = readFileSync(join(tmp, '.claude', 'skills', 'i-audit', 'SKILL.md'), 'utf8');
+    expect(content).toContain('name: i-audit');
+    expect(content).toContain('/i-polish');
+    expect(content).toContain('the i-impeccable skill');
+  });
+});
+
+// ─── Full install e2e (with real npx skills) ─────────────────────────────────
+
+let hasNpxSkills = false;
+try {
+  execSync('npx skills --version', { encoding: 'utf8', timeout: 15000, stdio: 'pipe' });
+  hasNpxSkills = true;
+} catch {}
+
+const describeNpx = hasNpxSkills ? describe : describe.skip;
+
+describeNpx('skills install: full e2e with npx skills', () => {
+  let tmp;
+
+  beforeAll(() => {
+    tmp = mkdtempSync(join(tmpdir(), 'imp-test-full-'));
+    execSync('git init', { cwd: tmp });
+  });
+
+  afterAll(() => {
+    if (tmp) rmSync(tmp, { recursive: true, force: true });
+  });
+
+  test('installs skills into a fresh project', () => {
+    const output = run('skills install -y', { cwd: tmp });
+    expect(output).toContain('Done!');
+
+    const hasSkills = ['.claude', '.cursor'].some(d => {
+      const dir = join(tmp, d, 'skills');
+      return existsSync(dir) && readdirSync(dir).length > 0;
+    });
+    expect(hasSkills).toBe(true);
+  }, 90000);
+
+  test('install with --prefix= renames all skills', () => {
+    const output = run('skills install -y --force --prefix=x-', { cwd: tmp });
+
+    // Find the provider that has skills
+    let found = false;
+    for (const d of ['.claude', '.cursor', '.gemini', '.codex', '.agents', '.kiro']) {
+      const dir = join(tmp, d, 'skills');
+      if (!existsSync(dir)) continue;
+      const skills = readdirSync(dir);
+      if (skills.length === 0) continue;
+      found = true;
+      const prefixed = skills.filter(s => s.startsWith('x-'));
+      // All skills should be prefixed
+      expect(prefixed.length).toBe(skills.length);
+      break;
+    }
+    expect(found).toBe(true);
+  }, 90000);
+});

wrangler.toml 🔗

@@ -0,0 +1,3 @@
+name = "impeccable"
+compatibility_date = "2024-12-01"
+pages_build_output_dir = "./build"