@struq/skill 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -2
  3. package/bin/struq.mjs +132 -0
  4. package/package.json +41 -4
  5. package/skills/struq/SKILL.md +237 -0
  6. package/skills/struq/cinematic/README.md +409 -0
  7. package/skills/struq/cinematic/checklists/01-preproduction.md +15 -0
  8. package/skills/struq/cinematic/checklists/02-implementation.md +17 -0
  9. package/skills/struq/cinematic/checklists/03-polish.md +17 -0
  10. package/skills/struq/cinematic/examples/code/nextjs/README.md +13 -0
  11. package/skills/struq/cinematic/examples/code/nextjs/app/cinematic-experience.tsx +13 -0
  12. package/skills/struq/cinematic/examples/code/nextjs/app/components/LenisRoot.tsx +42 -0
  13. package/skills/struq/cinematic/examples/code/nextjs/app/page.tsx +6 -0
  14. package/skills/struq/cinematic/examples/code/shared/CameraRig.tsx +65 -0
  15. package/skills/struq/cinematic/examples/code/shared/ExperienceCanvas.tsx +20 -0
  16. package/skills/struq/cinematic/examples/code/shared/ExperienceDirector.tsx +57 -0
  17. package/skills/struq/cinematic/examples/code/shared/ExperienceOverlay.tsx +37 -0
  18. package/skills/struq/cinematic/examples/code/shared/ExperiencePage.tsx +52 -0
  19. package/skills/struq/cinematic/examples/code/shared/GsapFrameLoop.tsx +32 -0
  20. package/skills/struq/cinematic/examples/code/shared/InteriorRoot.tsx +30 -0
  21. package/skills/struq/cinematic/examples/code/shared/README.md +20 -0
  22. package/skills/struq/cinematic/examples/code/shared/SubjectRig.tsx +58 -0
  23. package/skills/struq/cinematic/examples/code/shared/build-experience-timeline.ts +237 -0
  24. package/skills/struq/cinematic/examples/code/shared/direction.ts +52 -0
  25. package/skills/struq/cinematic/examples/code/shared/gsap-client.ts +32 -0
  26. package/skills/struq/cinematic/examples/code/shared/math.ts +12 -0
  27. package/skills/struq/cinematic/examples/code/shared/registry.ts +58 -0
  28. package/skills/struq/cinematic/examples/code/shared/runtime.ts +52 -0
  29. package/skills/struq/cinematic/examples/code/shared/styles.css +127 -0
  30. package/skills/struq/cinematic/examples/code/shared/useExperienceInput.ts +42 -0
  31. package/skills/struq/cinematic/examples/code/vite/README.md +14 -0
  32. package/skills/struq/cinematic/examples/code/vite/src/App.tsx +11 -0
  33. package/skills/struq/cinematic/examples/code/vite/src/LenisRoot.tsx +37 -0
  34. package/skills/struq/cinematic/examples/code/vite/src/main.tsx +9 -0
  35. package/skills/struq/cinematic/examples/concise/01-chapter-spec-example.md +42 -0
  36. package/skills/struq/cinematic/examples/concise/02-handoff-example.md +25 -0
  37. package/skills/struq/cinematic/examples/concise/03-audit-finding-example.md +13 -0
  38. package/skills/struq/cinematic/examples/verbose/01-full-experience-direction.md +687 -0
  39. package/skills/struq/cinematic/examples/verbose/02-full-audit-example.md +362 -0
  40. package/skills/struq/cinematic/references/01-core-mental-model.md +221 -0
  41. package/skills/struq/cinematic/references/02-vocabulary.md +384 -0
  42. package/skills/struq/cinematic/references/03-experience-architecture.md +302 -0
  43. package/skills/struq/cinematic/references/04-timeline-choreography.md +264 -0
  44. package/skills/struq/cinematic/references/05-handoff-contracts.md +241 -0
  45. package/skills/struq/cinematic/references/06-three-rig-architecture.md +286 -0
  46. package/skills/struq/cinematic/references/07-stack-integration.md +228 -0
  47. package/skills/struq/cinematic/references/08-gsap-plugin-patterns.md +218 -0
  48. package/skills/struq/cinematic/references/09-tuning-harness.md +202 -0
  49. package/skills/struq/cinematic/references/10-quality-systems.md +206 -0
  50. package/skills/struq/cinematic/references/11-agent-workflows.md +246 -0
  51. package/skills/struq/cinematic/references/12-audit-rubric.md +176 -0
  52. package/skills/struq/cinematic/references/13-official-docs.md +53 -0
  53. package/skills/struq/cinematic/templates/01-experience-brief.md +105 -0
  54. package/skills/struq/cinematic/templates/02-experience-contract.md +80 -0
  55. package/skills/struq/cinematic/templates/03-chapter-spec.md +103 -0
  56. package/skills/struq/cinematic/templates/04-handoff-contract.md +102 -0
  57. package/skills/struq/cinematic/templates/05-ownership-matrix.md +29 -0
  58. package/skills/struq/cinematic/templates/06-shot-presets.md +59 -0
  59. package/skills/struq/cinematic/templates/07-implementation-slice.md +66 -0
  60. package/skills/struq/cinematic/templates/08-tuning-register.md +36 -0
  61. package/skills/struq/cinematic/templates/09-acceptance-tests.md +57 -0
  62. package/skills/struq/references/anti-patterns.md +265 -0
  63. package/skills/struq/references/baseline-prompt.md +118 -0
  64. package/skills/struq/references/brand.md +143 -0
  65. package/skills/struq/references/brief-router.md +219 -0
  66. package/skills/struq/references/composition.md +191 -0
  67. package/skills/struq/references/depth-parallax.md +235 -0
  68. package/skills/struq/references/direction.md +188 -0
  69. package/skills/struq/references/feel-layer.md +107 -0
  70. package/skills/struq/references/gsap-api.md +293 -0
  71. package/skills/struq/references/iconography.md +255 -0
  72. package/skills/struq/references/imagery.md +144 -0
  73. package/skills/struq/references/loaders.md +287 -0
  74. package/skills/struq/references/micro-interactions.md +270 -0
  75. package/skills/struq/references/mobile-composition.md +70 -0
  76. package/skills/struq/references/nav-header.md +296 -0
  77. package/skills/struq/references/page-architecture.md +134 -0
  78. package/skills/struq/references/page-entrances.md +198 -0
  79. package/skills/struq/references/performance.md +235 -0
  80. package/skills/struq/references/pinning-scrub.md +341 -0
  81. package/skills/struq/references/pitfalls.md +290 -0
  82. package/skills/struq/references/route-transitions.md +281 -0
  83. package/skills/struq/references/scaling.md +151 -0
  84. package/skills/struq/references/scroll-reveals.md +280 -0
  85. package/skills/struq/references/seo.md +219 -0
  86. package/skills/struq/references/setup.md +397 -0
  87. package/skills/struq/references/showcase.md +87 -0
  88. package/skills/struq/references/stack-targets.md +78 -0
  89. package/skills/struq/references/struq-imagery.md +107 -0
  90. package/skills/struq/references/svg-motion.md +390 -0
  91. package/skills/struq/references/transitions.md +160 -0
  92. package/skills/struq/references/triage.md +71 -0
  93. package/skills/struq/references/typography.md +720 -0
  94. package/skills/struq/references/vibe-presets.md +287 -0
  95. package/skills/struq/references/webgl-r3f.md +274 -0
  96. package/skills/struq/references/webgl-scenes.md +593 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roy Heilbron
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,134 @@
1
- # Temporary Holding Version
1
+ # Struq
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Award-tier web design for your coding agent.**
4
+
5
+ Struq is a design skill for Claude Code, Codex, Cursor and OpenCode. Give it a brief and it builds a cinematic, agency-grade site: the kind with large light type, a restrained palette, smooth scroll, masked reveals, a pinned showpiece, full-screen page transitions and, where it earns its place, a 3D moment. It ships the decisions an award-winning studio makes, in the order they make them, on the stack they use.
6
+
7
+ The icon set the skill uses by default, Struq Icons, is browsable at [struq.nl/iconen](https://struq.nl/iconen).
8
+
9
+ ```bash
10
+ npx @struq/skill@latest
11
+ ```
12
+
13
+ It finds the AI tools on your machine and installs the skill into each of them. Then, in your agent:
14
+
15
+ ```text
16
+ /struq a portfolio for an architecture studio in Rotterdam, dark, calm, lots of photography
17
+ ```
18
+
19
+ | Agent | How to invoke |
20
+ |---|---|
21
+ | Claude Code (installed with `npx @struq/skill` or `npx skills`) | `/struq <brief>` |
22
+ | Claude Code (installed as a plugin) | `/struq:struq <brief>` |
23
+ | Codex | `$struq <brief>` |
24
+ | Cursor | invoke the struq skill by name: "use the struq skill to build ..." |
25
+ | OpenCode | invoke the struq skill by name: "use the struq skill to build ..." |
26
+
27
+ ## Same brief, with and without Struq
28
+
29
+ Three briefs, each built twice by the same model with the same materials: once as a plain coding agent, once with `/struq`. An independent reviewer then scored all six sites **blind**, from screenshots only, without knowing which side used the skill.
30
+
31
+ | Brief | Without | With Struq | Invented facts (without / with) | Template tells (without / with) |
32
+ |---|---|---|---|---|
33
+ | Ceramicist portfolio, no logo, no photos | 56 | **79** | 14 / 5 | 6 / 3 |
34
+ | E-bike launch, existing brand, wants 3D | 66 | **76** | 11 / 3 | 6 / 3 |
35
+ | Open-source dev-tool landing page | 50 | **76** | 11 / 2 | 10 / 2 |
36
+ | **Average** | **57** | **77** | **36 / 10** | **22 / 8** |
37
+
38
+ Scored out of 100 on typography, composition, color, concept, motion, content honesty, craft and distinctiveness. "Invented facts" counts claims the brief never gave: quotes, bios, stats, specs, API names.
39
+
40
+ **Dev-tool landing page.** Without: the dark developer-tool template, item for item. With: a light page built around a trace waterfall that assembles as you scroll.
41
+
42
+ | Without | With Struq |
43
+ |---|---|
44
+ | ![Tidewater without Struq](docs/compare/saas-plain.webp) | ![Tidewater with Struq](docs/compare/saas-struq.webp) |
45
+
46
+ **Ceramicist portfolio.** Without: a tidy template with a stat strip, a chef quote and a bio the brief never supplied. With: a single-line profile drawing of each piece stands in for the missing photos and becomes the site's motif.
47
+
48
+ | Without | With Struq |
49
+ |---|---|
50
+ | ![Ana Ferreira without Struq](docs/compare/ceramics-plain.webp) | ![Ana Ferreira with Struq](docs/compare/ceramics-struq.webp) |
51
+
52
+ **E-bike launch.** The closest pair: both built a pinned 3D bike. Without: a spec sheet the brand never published. With: the lime belt as the one motif, and unknown specs openly held back.
53
+
54
+ | Without | With Struq |
55
+ |---|---|
56
+ | ![Vela Mono without Struq](docs/compare/ebike-plain.webp) | ![Vela Mono with Struq](docs/compare/ebike-struq.webp) |
57
+
58
+ Where Struq lost points: craft. The plain builds are less ambitious and break less; the Struq builds had reveal states that rendered empty in some captures. That is the next thing being fixed.
59
+
60
+ ## What you get
61
+
62
+ **One recipe, fixed on purpose.** The expensive feel comes from these pieces sharing one animation clock, not from any single effect.
63
+
64
+ | Layer | Tooling |
65
+ |---|---|
66
+ | Typography | Two faces from the Signature pairing set, set through role tokens. Display runs large and light (300 to 400). A fluid scale, one typographic signature move per site, SplitText choreography. |
67
+ | Palette | Temperature-shifted neutrals, one accent, light and dark surface ladders |
68
+ | Scroll | Lenis on the GSAP ticker, ScrollTrigger scrub, masked reveals, never opacity-only fades |
69
+ | Showpieces | 23 named recipes, each tagged showpiece or system: pinned scrub timelines, horizontal galleries, sticky stacks, path journeys, manifesto fills |
70
+ | SVG | DrawSVG and MorphSVG: self-drawing marks, logo morphs, scroll-drawn paths |
71
+ | Pointer | Magnetic buttons, cursor followers, hover scenes, image distortion |
72
+ | Page flow | Full-screen cover, swap and reveal route transitions, session-gated loaders, Radix overlay menus |
73
+ | 3D | Three.js, React Three Fiber, Drei and postprocessing as lazy islands with poster fallbacks |
74
+ | Direction | A directed mode for full WebGL experiences: chapters, camera shots, handoffs, ownership |
75
+
76
+ Stack: Next.js App Router and Tailwind by default, Vite supported. It reads the versions your project has installed and adapts to them; nothing is pinned. Every GSAP plugin used is free on npm. Icons: [Struq Icons](https://struq.nl/iconen) (`@struq/icons`, MIT) by default.
77
+
78
+ ## Commands
79
+
80
+ | Command | What it does |
81
+ |---|---|
82
+ | `/struq <brief>` | Plans the site into one `STRUQ.md`, stops once for your veto, builds it, then screenshots it at 390, 768, 1440 and 1920 and fixes what it sees |
83
+ | `/struq redesign <path>` | Keeps your brand, copy and components; rebuilds the execution |
84
+ | `/struq audit <path-or-url>` | Changes nothing. Returns a score out of 100, every tell with its fix, and the three highest-payoff changes |
85
+ | `/struq direct <brief>` | Directed mode for one continuous 3D experience across chapters |
86
+
87
+ In Codex, use `$struq` instead of `/struq`; as a Claude Code plugin the command is `/struq:struq`.
88
+
89
+ ## How a build runs
90
+
91
+ 1. **Plan.** It reads your brief and writes one `STRUQ.md`: brand, concept, the two typefaces, palette tokens, icon family, page arc, which showcase recipes go where, the transition system, and whether 3D is warranted. It never invents testimonials or stats.
92
+ 2. **One stop.** It shows the summary and asks once: veto anything, and send a logo, photos or real numbers if you have them. Only an empty or vague brief gets a short wizard first.
93
+ 3. **Build.** Static layout to the baseline first, at desktop and mobile at once. Motion goes on in layers: reveals, composition, showpieces, text and SVG, pointer, then 3D.
94
+ 4. **Look.** Screenshots at four widths, a tell scan, a feel audit and one full scroll. Then it fixes and re-shoots.
95
+
96
+ ## Install options
97
+
98
+ ```bash
99
+ # every AI tool on this machine: Claude Code, Cursor, Codex, OpenCode, Gemini CLI, Copilot, Windsurf
100
+ npx @struq/skill@latest
101
+
102
+ # one tool only, or into the current project instead of your home folder
103
+ npx @struq/skill@latest --claude
104
+ npx @struq/skill@latest --project
105
+ ```
106
+
107
+ Run it again to update, and `npx @struq/skill@latest remove` to take it out.
108
+
109
+ With the open skills CLI, which knows 70+ agents:
110
+
111
+ ```bash
112
+ npx skills add struqnl/struq-design -g
113
+ ```
114
+
115
+ As a Claude Code plugin (the command is then `/struq:struq`):
116
+
117
+ ```text
118
+ /plugin marketplace add struqnl/struq-design
119
+ /plugin install struq@struq
120
+ ```
121
+
122
+ Node 18+ is required. The screenshot step uses whatever browser tool your agent has (Playwright or similar).
123
+
124
+ ## Struq Icons
125
+
126
+ Struq builds use [Struq Icons](https://struq.nl/iconen) by default: 182 two-tone icons, most with their own hover motion, MIT.
127
+
128
+ ```bash
129
+ npm install @struq/icons
130
+ ```
131
+
132
+ ## License
133
+
134
+ MIT
package/bin/struq.mjs ADDED
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ // npx @struq/skill@latest: copies the Struq skill into every AI coding tool found on this machine.
3
+ // No dependencies, no prompts (an agent can run it unattended), and nothing is fetched beyond
4
+ // this package: the skill ships inside it, in skills/struq.
5
+
6
+ import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
7
+ import { homedir } from 'node:os';
8
+ import { dirname, join, relative, sep } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
12
+ const SOURCE = join(PACKAGE_ROOT, 'skills', 'struq');
13
+ const { version } = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8'));
14
+ const NAME = 'struq';
15
+
16
+ // Where each tool reads skills, matching the open skills CLI (github.com/vercel-labs/skills,
17
+ // docs/agents.md). `home` is the folder whose presence means the tool is installed.
18
+ const TOOLS = [
19
+ { flag: 'claude', label: 'Claude Code', home: '.claude', global: '.claude/skills', project: '.claude/skills', use: '/struq <brief>' },
20
+ { flag: 'cursor', label: 'Cursor', home: '.cursor', global: '.cursor/skills', project: '.agents/skills', use: 'ask for the struq skill by name' },
21
+ { flag: 'codex', label: 'Codex', home: '.codex', global: '.agents/skills', project: '.agents/skills', use: '$struq <brief>' },
22
+ { flag: 'opencode', label: 'OpenCode', home: '.config/opencode', global: '.config/opencode/skills', project: '.agents/skills', use: 'ask for the struq skill by name' },
23
+ { flag: 'gemini', label: 'Gemini CLI', home: '.gemini', global: '.gemini/skills', project: '.agents/skills', use: 'ask for the struq skill by name' },
24
+ { flag: 'copilot', label: 'GitHub Copilot', home: '.copilot', global: '.copilot/skills', project: '.agents/skills', use: 'ask for the struq skill by name' },
25
+ { flag: 'windsurf', label: 'Windsurf', home: '.codeium/windsurf', global: '.codeium/windsurf/skills', project: '.windsurf/skills', use: 'ask for the struq skill by name' },
26
+ ];
27
+
28
+ const HELP = `Struq skill ${version}: websites at the level of a design studio, built by your own AI.
29
+
30
+ Usage
31
+ npx @struq/skill@latest install for every AI tool found on this machine
32
+ npx @struq/skill@latest --claude only for one tool: ${TOOLS.map((t) => `--${t.flag}`).join(', ')}
33
+ npx @struq/skill@latest --project into this project instead of your home folder
34
+ npx @struq/skill@latest remove remove the skill again (takes the same flags)
35
+ npx @struq/skill@latest --help show this
36
+
37
+ Run it again at any time to update. More: https://struq.nl/skill
38
+ `;
39
+
40
+ function parse(argv) {
41
+ const options = { command: 'install', project: false, only: [] };
42
+ for (const arg of argv) {
43
+ if (arg === 'remove' || arg === 'uninstall') options.command = 'remove';
44
+ else if (arg === 'install' || arg === 'add') options.command = 'install';
45
+ else if (arg === '--help' || arg === '-h' || arg === 'help') options.command = 'help';
46
+ else if (arg === '--version' || arg === '-v') options.command = 'version';
47
+ else if (arg === '--project' || arg === '-p') options.project = true;
48
+ else if (arg === '--global' || arg === '-g') options.project = false;
49
+ else {
50
+ const tool = TOOLS.find((t) => arg === `--${t.flag}`);
51
+ if (!tool) throw new Error(`Unknown option "${arg}". Run npx @struq/skill@latest --help.`);
52
+ options.only.push(tool);
53
+ }
54
+ }
55
+ return options;
56
+ }
57
+
58
+ /** The tools to install for, each with the folder its copy goes to. Two tools can share one folder. */
59
+ function targets({ project, only }) {
60
+ const home = homedir();
61
+ const base = project ? process.cwd() : home;
62
+ let picked = only.length ? only : TOOLS.filter((t) => existsSync(join(home, t.home)));
63
+ // Nothing detected: Claude Code is the most common host, and its folder is the one the
64
+ // skill's own docs point at, so that is where a first install goes.
65
+ const guessed = !picked.length;
66
+ if (guessed) picked = [TOOLS[0]];
67
+ return { guessed, list: picked.map((tool) => ({ tool, dir: join(base, project ? tool.project : tool.global, NAME) })) };
68
+ }
69
+
70
+ const shown = (path) => {
71
+ const fromHome = relative(homedir(), path);
72
+ return fromHome && !fromHome.startsWith('..') ? `~${sep}${fromHome}` : path;
73
+ };
74
+
75
+ /** Removes our folder or the link to it, never anything else: a link is unlinked, its target left alone. */
76
+ function clear(dir) {
77
+ if (!existsSync(dir) && !isLink(dir)) return false;
78
+ rmSync(dir, { recursive: !isLink(dir), force: true });
79
+ return true;
80
+ }
81
+
82
+ function isLink(path) {
83
+ try {
84
+ return lstatSync(path).isSymbolicLink();
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+
90
+ function run(argv) {
91
+ const options = parse(argv);
92
+ if (options.command === 'help') return void process.stdout.write(HELP);
93
+ if (options.command === 'version') return void process.stdout.write(`${version}\n`);
94
+
95
+ const { guessed, list } = targets(options);
96
+ const done = new Set();
97
+ const lines = [];
98
+
99
+ for (const { tool, dir } of list) {
100
+ if (options.command === 'remove') {
101
+ const removed = done.has(dir) || clear(dir);
102
+ done.add(dir);
103
+ lines.push(` ${removed ? '-' : ' '} ${tool.label.padEnd(15)}${removed ? shown(dir) : 'was not installed'}`);
104
+ continue;
105
+ }
106
+ if (!done.has(dir)) {
107
+ clear(dir);
108
+ mkdirSync(dirname(dir), { recursive: true });
109
+ cpSync(SOURCE, dir, { recursive: true });
110
+ done.add(dir);
111
+ }
112
+ lines.push(` ✓ ${tool.label.padEnd(15)}${shown(dir)}`);
113
+ }
114
+
115
+ const out = [`Struq skill ${version}`, '', ...lines, ''];
116
+ if (options.command === 'install') {
117
+ if (guessed) out.push('No AI tool found, so it went to Claude Code. Use --cursor, --codex and so on for another tool.', '');
118
+ out.push('Restart your AI tool, then start with:');
119
+ for (const tool of new Map(list.map(({ tool }) => [tool.label, tool])).values()) {
120
+ out.push(` ${tool.label.padEnd(15)}${tool.use}`);
121
+ }
122
+ out.push('', 'Run this again at any time to update. More: https://struq.nl/skill');
123
+ }
124
+ process.stdout.write(`${out.join('\n')}\n`);
125
+ }
126
+
127
+ try {
128
+ run(process.argv.slice(2));
129
+ } catch (error) {
130
+ process.stderr.write(`struq: ${error.message}\n`);
131
+ process.exit(1);
132
+ }
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "@struq/skill",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.1",
4
+ "description": "Install the free Struq skill into Claude Code, Cursor, Codex and other AI coding tools: websites at the level of a design studio, built by your own AI.",
5
+ "license": "MIT",
6
+ "author": "Struq (https://struq.nl)",
7
+ "homepage": "https://struq.nl/skill",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/struqnl/struq-design.git"
11
+ },
12
+ "bugs": "https://github.com/struqnl/struq-design/issues",
13
+ "keywords": [
14
+ "agent-skill",
15
+ "skills",
16
+ "claude-code",
17
+ "cursor",
18
+ "codex",
19
+ "web-design",
20
+ "gsap",
21
+ "awwwards",
22
+ "struq"
23
+ ],
24
+ "type": "module",
25
+ "bin": {
26
+ "struq": "bin/struq.mjs"
27
+ },
28
+ "files": [
29
+ "bin",
30
+ "skills",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "scripts": {
38
+ "test": "node --test test/install.test.mjs"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ }
43
+ }
@@ -0,0 +1,237 @@
1
+ ---
2
+ name: struq
3
+ description: "Award-tier web design for coding agents. Builds cinematic, agency-grade marketing sites, landing pages and portfolios at FWA / Awwwards level: two-face typography with light display weights, curated palettes, Lenis smooth scroll on one GSAP clock, SplitText, DrawSVG and MorphSVG, masked reveals, pinned and horizontal showpieces, hover choreography, full-screen route transitions, Radix overlays, and Three.js / R3F / Drei / postprocessing moments. Use when designing, building, redesigning, animating or auditing a marketing or narrative site, or when a page must feel premium, custom, cinematic or award-winning instead of templated. Stack: Next.js App Router + Tailwind v4 or later (Vite supported), versions detected from the project."
4
+ argument-hint: "[brief] | redesign <path> | audit <path-or-url> | direct <brief>"
5
+ ---
6
+
7
+ # Struq
8
+
9
+ The gap between "looks like a template" and "looks like an award-winning agency build" is not talent. It is a finite set of decisions, made in the right order, executed on the right stack. Struq is those decisions. Apply the baseline to **every** build and load reference files only for the techniques the page actually uses.
10
+
11
+ **Register check.** Struq is for marketing, landing, portfolio and narrative sites: the cinematic, scroll-driven, agency register. For dashboards and product UI, keep the type and surface rules from the baseline, drop Lenis, cinematic durations and showpieces. Density and native scroll win there.
12
+
13
+ ## Modes
14
+
15
+ Read the argument and pick one. State the mode in one line, then work.
16
+
17
+ | Invocation | Mode | What happens |
18
+ |---|---|---|
19
+ | `/struq <brief>` (or no argument) | **Build** | Run the Direction phases into `STRUQ.md`, stop once for a veto, build, verify. |
20
+ | `/struq redesign <path>` | **Redesign** | Audit assets and performance, triage the current site ([references/triage.md](references/triage.md)), then Direction with the brand kept, then build. |
21
+ | `/struq audit <path-or-url>` | **Audit** | No code changes. Screenshot, run the tell scan and feel audit, return a scored report (see Audit below). |
22
+ | `/struq direct <brief>` | **Directed** | One continuous WebGL experience across chapters (persistent stage, camera shots, handoffs). Run Direction phases 0 to 3 into `STRUQ.md`, then enter [cinematic/README.md](cinematic/README.md), which appends its direction to the same file. Use only when the brief is genuinely an "experience", not a site with a 3D hero. |
23
+
24
+ **Plan everything before the first component.** Build and Redesign always start with [references/direction.md](references/direction.md): intake, brand (name, SVG logo and mark, when there is none), concept (the one idea and its motif), system (two typefaces, palette, icon family, imagery, motion), and the site plan (pages, arc, showpieces, transitions, SEO). It all goes into one file, `STRUQ.md` in the project root, which is the only plan artifact and the contract for the rest of the build. Stop once, at the gate, then build.
25
+
26
+ **Ask for materials, never invent them.** Use the logo, copy, photos and numbers that exist. With a usable brief, do not stop to ask first: plan with what is there, and put the materials request inside the one gate message ("Veto anything? If you have a logo, photos or real numbers, send them now."). Where something is still missing, write real, specific running copy for the brief and plan imagery from one Struq beeldbank pack ([references/struq-imagery.md](references/struq-imagery.md)) or as graded placeholders ([references/imagery.md](references/imagery.md) §4b). Never lorem, never "Acme", never invented testimonials, logos or stats. Never write in a real person's or brand's voice: no invented bio, process claims, quotes or catalogue facts; anything the layout needs before the real copy arrives is a labelled draft listed under ASSUMPTIONS ([references/direction.md](references/direction.md) §1).
27
+
28
+ ## The recipe
29
+
30
+ This is the stack and the layer each tool owns. It is fixed on purpose: the "weighted, expensive" feel comes from these pieces sharing one clock, not from any single effect.
31
+
32
+ | Layer | Tool | Rule of thumb | Reference |
33
+ |---|---|---|---|
34
+ | Brand | Name, SVG wordmark and mark, favicon set | Positioning before name; the wordmark is drawn from the display face | [brand.md](references/brand.md) |
35
+ | Typography | Signature first: one pairing id (S1 to S7), one display face and one text face, set through role tokens | Display runs **large and light (300 to 400)**. Hierarchy from scale contrast, not weight. One signature move per site. | [typography.md](references/typography.md) |
36
+ | Palette | Tokens on `:root` via Tailwind `@theme` (v4 or later) | Temperature-shifted neutrals do 95% of the work, one accent | [vibe-presets.md](references/vibe-presets.md) |
37
+ | Scale | One fluid root-rem knob | Everything in rem; breakpoints change layout, never size | [scaling.md](references/scaling.md) |
38
+ | Smooth scroll | Lenis, driven by `gsap.ticker` | One requestAnimationFrame for the whole page | [setup.md](references/setup.md), [feel-layer.md](references/feel-layer.md) |
39
+ | Scroll motion | GSAP + ScrollTrigger | Scrub for scroll-tied values; masked reveals over fades | [scroll-reveals.md](references/scroll-reveals.md) |
40
+ | Text motion | SplitText | Masked line rise, char stagger, scroll color fill | [typography.md](references/typography.md) |
41
+ | SVG motion | DrawSVG, MorphSVG, MotionPath | Draw-on-scroll paths, logo morphs, signature marks | [svg-motion.md](references/svg-motion.md) |
42
+ | Showpieces | Pinned scrub, horizontal galleries, sticky stacks | One or two per site, placed at act breaks | [showcase.md](references/showcase.md), [pinning-scrub.md](references/pinning-scrub.md) |
43
+ | Icons | Struq Icons (`@struq/icons`) by default, after the brand's own set; else Phosphor (light), else Iconoir or Tabler | One icon family per site, never mixed, never Lucide by reflex | [iconography.md](references/iconography.md) |
44
+ | Hover and pointer | GSAP `quickTo` (magnetic, cursor), CSS + GSAP hover scenes | Garnish, applied last | [micro-interactions.md](references/micro-interactions.md) |
45
+ | React lifecycle | Motion (`motion/react`) | Mount/unmount, `layoutId`, gesture state | GSAP vs Motion below |
46
+ | Overlays | Radix primitives (Dialog for menus and modals) | Focus trap, Escape, scroll lock paired with `lenis.stop()` | [nav-header.md](references/nav-header.md) |
47
+ | Page flow | Full-screen cover, swap, reveal transitions; session-gated loader | Every route change is a designed moment | [route-transitions.md](references/route-transitions.md), [loaders.md](references/loaders.md) |
48
+ | 3D moments | Three.js, R3F, Drei, postprocessing | Lazy island, poster fallback, driven from the same ticker | [webgl-r3f.md](references/webgl-r3f.md), [webgl-scenes.md](references/webgl-scenes.md) |
49
+ | Findability | Metadata API, an OG image per route, sitemap, robots, JSON-LD | One h1 per page; loaders and reveals never hide content from the HTML | [seo.md](references/seo.md) |
50
+ | Performance | ffmpeg, next/image, gltf-transform, next/font | Assets first: budgets per role, posters always, WebGL never in the first-load bundle | [performance.md](references/performance.md) |
51
+
52
+ Install line and provider wiring: [references/setup.md](references/setup.md). All GSAP plugins are free on npm from GSAP 3.13 on (the minimum version); no token, no club. No other version is assumed: the installed project decides ([references/setup.md](references/setup.md) §0).
53
+
54
+ ## The baseline (applies to everything)
55
+
56
+ <!-- CANONICAL: this is the single source of the baseline block. references/baseline-prompt.md carries a standalone copy for pasting into other agents; after editing here, re-copy the block there. -->
57
+
58
+ ```
59
+ STRUQ BASELINE: NON-NEGOTIABLES
60
+
61
+ TYPE: Signature first: one pairing id from typography.md §3.1 (S1 to S7;
62
+ Extended E1 to E6 only when no Signature temperament fits), two faces max.
63
+ Never Inter, Roboto, Open Sans, Poppins, Montserrat or Lato; never Space
64
+ Grotesk or Geist as a reflex. Scale 1.333 with skipped steps (body 1, lead
65
+ 1.25, h3 1.78, h2 3.16, display 7 to 8). Hero h1 = text-display,
66
+ clamp(2.875rem, 1.2rem + 6.86vw, 8rem), leading 0.92 to 1.0; S4 and S5 set
67
+ larger, S7 smaller (typography.md §3.1). Display runs LIGHT (300 to 400);
68
+ heavy display only as a declared genre (the Brutalist appendix), never a
69
+ reflex. Tracking per face, never one tracking-tight on every size: sans
70
+ display about -0.025 to -0.03em, serif display about -0.01 to -0.025em, h2
71
+ about half that. Body 1rem to 1.0625rem at 400, leading 1.6+, 55 to 75ch;
72
+ emphasis 500, never 700. Hierarchy from scale contrast, not medium sizes.
73
+ ROLES: every string takes a role token, never text-xs, text-lg or text-5xl:
74
+ font-display text-display, font-display text-h2, type-h3 text-h3,
75
+ text-lead, text-body, text-small, eyebrow text-eyebrow, font-ui text-ui,
76
+ stat text-stat, and font-display text-statement for the one Orient line
77
+ or manifesto. Eyebrows: uppercase 0.75rem (the 12px floor), tracked 0.12
78
+ to 0.18em per face, ink-muted, one per section, never ink-faint.
79
+ FLOOR: body at least 1rem, muted at least 0.875rem, nothing under 12px.
80
+ CONTRAST: body at least 4.5:1, large text at least 3:1. ink-faint is never
81
+ text: hairlines and disabled states only.
82
+ SIGNATURE: every STRUQ.md declares one typographic signature move
83
+ (typography.md §5: weight-in, soft-scrub, type-mask-media,
84
+ fit-width-wordmark, a cropped display). The masked line rise is a
85
+ technique, not the default hero. Mono only for literal code. Headings get
86
+ text-wrap: balance; body gets text-wrap: pretty.
87
+
88
+ SPACING: Base-8 rhythm with deliberate asymmetry. Section padding varies by
89
+ role: hero 12 to 20rem, dense content 6 to 8rem, breathers 16 to 24rem. NEVER
90
+ uniform py-16 on every section. Whitespace is a material: at least one section
91
+ should feel "too empty" by SaaS standards.
92
+
93
+ SCALING: One fluid root-rem knob, not per-breakpoint size ladders:
94
+ html { font-size: clamp(1rem, 0.92rem + 0.2vw, 1.125rem); }
95
+ 16px at 390, about 17.6px at 1440, frozen at 18px from about 1640. Every
96
+ clamp() keeps a rem term in its middle so zoom works; a vw-only middle is a
97
+ bug. Author everything in rem; px only for 1px hairlines. Breakpoints change
98
+ LAYOUT (columns, stack or row), never size. Display roles carry their own
99
+ clamp() on top.
100
+
101
+ SURFACE: Elevation is a tonal step, not a drop shadow. Dark mode: each layer
102
+ steps LIGHTER (#0A0A0B, #111113, #19191C, #232328). Light mode: pick one
103
+ direction and keep it (panels a shade deeper than a toned page, or white lifted
104
+ off a toned page). Hairline borders about 8 to 12% off the surface; in light
105
+ mode a border is the ink at low alpha (rgb(<ink> / 0.08 to 0.14)), never a
106
+ flat gray hex. Shadows only for true floating elements, then large, soft and faint
107
+ (0 24px 80px -12px rgb(0 0 0 / 0.4)). Never shadow-md or shadow-lg.
108
+
109
+ COLOR: One accent, used sparingly (links, the key CTA, one highlight per
110
+ viewport). Neutrals are temperature-shifted (warm cream and off-black, or cool
111
+ charcoal), never pure #FFF and #000 with gray-500 text. A second accent is a
112
+ declared genre choice with its own token and the same budget. No blue to
113
+ purple gradients, no gradient text, no glassmorphism cards.
114
+
115
+ MOTION: Lenis smooth scroll (lerp 0.09 to 0.11) on one gsap.ticker clock,
116
+ unless the genre calls for native scroll. Every entrance animates opacity AND
117
+ transform (y 24 to 80px, or a clip or overflow mask). Opacity-only fades are
118
+ forbidden. Stagger siblings 0.06 to 0.1s. Durations: micro 0.2 to 0.3s,
119
+ standard UI 0.3 to 0.6s, reveals 0.6 to 1.0s, hero and cinematic 1.0 to 1.6s;
120
+ exits run about 60% of their entrance. Eases: entrances expo-out
121
+ cubic-bezier(0.16, 1, 0.3, 1) (expo.out); exits power2.in; position swaps
122
+ quart-in-out cubic-bezier(0.76, 0, 0.24, 1) (power3.inOut); curtains and
123
+ panels quint-in-out cubic-bezier(0.83, 0, 0.17, 1) (power4.inOut). Never
124
+ default ease-in-out everywhere.
125
+ Scroll-tied effects scrub; they do not fire time-based tweens at a threshold.
126
+ prefers-reduced-motion: gate transforms, keep opacity.
127
+
128
+ COMPOSITION: Break the grid at least twice per page: an image bleeding off
129
+ the viewport, an element overlapping two sections, oversized type running off
130
+ canvas, a sticky element. No three identical icon cards in a row. Vary section
131
+ layout: after a 50/50 split comes full-bleed or an asymmetric 1/3 and 2/3.
132
+ Icons: ONE coherent family chosen for the brand, one stroke and size,
133
+ ink-colored. Never emoji, never the AI-cliche icons (Sparkles, Rocket, Zap),
134
+ never icons in colored chips. Animated icons run under 500ms on click and
135
+ never loop in standard UI.
136
+
137
+ DETAILS THAT SELL IT: Real running copy. Tracked uppercase eyebrows as
138
+ wayfinding, not 01/02/03 stamped on every section (one deliberate oversized
139
+ numeral is fine). The footer is designed as hard as the hero. Custom
140
+ ::selection color. Visible, designed focus-visible states. Every route change
141
+ is a designed transition, not a hard cut.
142
+ ```
143
+
144
+ Why each rule earns its place: [references/baseline-prompt.md](references/baseline-prompt.md).
145
+
146
+ ## Leverage ranking: spend effort in this order
147
+
148
+ Ordered by visual payoff divided by implementation cost. Do 1 to 7 on every build; 8 to 13 when the page calls for them.
149
+
150
+ 1. **Page architecture.** Which sections exist, in what order, and where the showpiece sits. Free, and it decides everything downstream. A perfect section inside a template arc still reads as a template. [references/page-architecture.md](references/page-architecture.md)
151
+ 2. **Typography.** The Signature pairing, the light display weight, the role tokens, the eyebrow system, the one signature move. This is 70% of "looks designed". Set the root-rem knob ([references/scaling.md](references/scaling.md)) first, then the type system ([references/typography.md](references/typography.md)).
152
+ 3. **Palette and surfaces.** Kills the "Tailwind default" look instantly. [references/vibe-presets.md](references/vibe-presets.md)
153
+ 4. **Masked, staggered scroll reveals.** The single highest-payoff motion technique, including the curtain section reveal. [references/scroll-reveals.md](references/scroll-reveals.md)
154
+ 5. **The feel layer.** Lenis lerp, scrub smoothing, ease selection. Wrong values make good techniques feel cheap. [references/feel-layer.md](references/feel-layer.md)
155
+ 6. **Nav and header.** On screen for the whole session; the fullscreen menu is often the most art-directed moment on the site. [references/nav-header.md](references/nav-header.md)
156
+ 7. **Imagery and art direction.** One grade, declared ratios, treated assets. Ungraded mixed stock undoes everything above it. [references/imagery.md](references/imagery.md). No photos of their own? Struq's beeldbank comes before stock: [references/struq-imagery.md](references/struq-imagery.md)
157
+ 8. **Composition: overlap, bleed, z-choreography.** [references/composition.md](references/composition.md), and how each technique translates at 360 to 430px: [references/mobile-composition.md](references/mobile-composition.md)
158
+ 9. **Showpieces.** Pinned scrub timelines, horizontal galleries, sticky stacks, path journeys. High cost; one or two per site at act breaks. Pick by id from [references/showcase.md](references/showcase.md).
159
+ 10. **Page flow.** Full-screen route transitions ([references/route-transitions.md](references/route-transitions.md)), a session-gated first-load loader ([references/loaders.md](references/loaders.md)), per-zone entrances ([references/page-entrances.md](references/page-entrances.md)), section curtains and match cuts ([references/transitions.md](references/transitions.md)). Loader invariant: the pre-paint revisit gate and the live animation phase are separate state machines. A persistent `seen` flag must never hide a curtain that is still playing.
160
+ 11. **Text and SVG choreography.** SplitText beyond the line reveal, DrawSVG, MorphSVG. [references/typography.md](references/typography.md), [references/svg-motion.md](references/svg-motion.md)
161
+ 12. **Hover and pointer.** Magnetic buttons, cursor followers, hover scenes. Garnish, not the meal. [references/micro-interactions.md](references/micro-interactions.md)
162
+ 13. **WebGL moments.** Highest cost of all; only when a section genuinely calls for it. Integration first ([references/webgl-r3f.md](references/webgl-r3f.md)), then Drei, postprocessing and interaction ([references/webgl-scenes.md](references/webgl-scenes.md)). If the whole page is one 3D experience, switch to Directed mode.
163
+
164
+ Before and after pairs for every category, the cheap reflex against the agency version: [references/anti-patterns.md](references/anti-patterns.md). **If output drifts generic, read that file first.**
165
+
166
+ **Something looks wrong in motion?** Read [references/pitfalls.md](references/pitfalls.md) first, before changing any code: the symptom-first bug clinic for the bugs that recur on every GSAP build: clipped descenders, jarring or jumping pins, content stuck invisible after a reveal, utilities that vanish on animated elements, entrances that fire before the image paints. Each entry gives the cause, the fix and the check.
167
+
168
+ **The page will not scroll at all?** It is one of exactly two bugs. Either nothing drives `lenis.raf()` because the ticker wiring lost a race with the `<ReactLenis>` ref, or an overlay left an unbalanced `lenis.stop()`. Check the `<html>` class list for `lenis-stopped` first; that one glance tells you which. Both fixes are in [references/setup.md](references/setup.md) §2 and §2b. Do not start "fixing" CSS overflow. A pinned section that jerks on engage is almost always `anticipatePin` with smooth scroll: [references/pinning-scrub.md](references/pinning-scrub.md) §2.
169
+
170
+ ## AI-default blocklist
171
+
172
+ These scream "generated". Never ship them:
173
+
174
+ - A three-column grid of identical `rounded-xl border shadow-sm` cards with a centered icon, an h3 and two lines of gray text
175
+ - Blue to purple (or teal to violet) gradients on backgrounds, buttons or `bg-clip-text` headlines
176
+ - Uniform `py-16` or `py-24` on every section, where every section is a centered h2, a subhead and a grid
177
+ - Off-list faces: Inter, Roboto, Open Sans, Poppins or Montserrat (or Lato, or the system stack) for everything, or Space Grotesk or Geist as the reflex "technical" face, instead of a Signature pairing
178
+ - Bold 700 display headlines, five medium heading sizes, one `tracking-tight` on every size, ad-hoc `text-xs uppercase tracking-[...]` eyebrows instead of the role tokens
179
+ - `text-ink-faint` on any text
180
+ - `shadow-md` or `shadow-lg` as the elevation system on gray-50 cards
181
+ - Opacity-only fade-ins, all fired at once, with default `ease-in-out` and `duration-300`
182
+ - `bg-white` page, `text-gray-500` body, `border-gray-200` everywhere
183
+ - Emoji as icons, the cliche trio (Sparkles, Rocket, Zap) as decoration, icons in colored circles
184
+ - Monospace as decoration on eyebrows, labels, stats or badges
185
+ - `01 / 02 / 03` stamped on every section or row
186
+ - Hero = centered h1, subhead, two buttons and a dashboard screenshot in browser chrome
187
+ - Untreated mixed-grade stock at random ratios, glossy AI-render blobs, floating purple spheres
188
+ - `backdrop-blur` glass cards on a mesh-gradient background
189
+ - Hard cuts between routes on a multi-page site
190
+ - Lorem ipsum, "John Doe", "Acme Inc.", "Your journey starts here"
191
+
192
+ ## GSAP vs Motion: the decision rule
193
+
194
+ Both are installed. Never animate the same property on the same element with both, and never with GSAP and a CSS `transition` either: one owner per property per element ([references/pitfalls.md](references/pitfalls.md) §3). **GSAP owns everything that runs every frame or is tied to scroll; Motion owns React lifecycle, layout and gesture state.** The reason is the single clock: the whole build runs on one requestAnimationFrame (Lenis driven by `gsap.ticker`, [references/setup.md](references/setup.md) §2). High-frequency pointer work belongs on that same ticker via `quickTo`. Pushing it onto a competing spring loop is exactly what makes good motion feel subtly off.
195
+
196
+ | GSAP + ScrollTrigger | Motion |
197
+ |---|---|
198
+ | Anything scroll-tied: scrub, pins, parallax, reveals | Mount and unmount (`AnimatePresence`) |
199
+ | Multi-step timelines and orchestration | Layout and shared-element swaps from React state (`layout`, `layoutId`) |
200
+ | SplitText, DrawSVG, MorphSVG, MotionPath | `whileHover`, `whileTap`, one-off `whileInView` |
201
+ | Flip morphs that coordinate with scroll or a timeline | Press and toggle micro-feedback |
202
+ | Magnetic buttons, custom cursor (`quickTo`) | Local stateful overlays that are not part of a timeline |
203
+ | Horizontal scroll, sticky stacks, R3F uniforms from scroll | |
204
+
205
+ **Before writing any Flip,** read [references/gsap-api.md](references/gsap-api.md) §7.1 to 7.4. Flip has four failure modes that compound and that no typecheck catches. Flip is verified by watching it, never by a green build.
206
+
207
+ ## Workflow (Build and Redesign)
208
+
209
+ These step names are the one numbering scheme; [references/brief-router.md](references/brief-router.md) §4 keys its load manifest to them.
210
+
211
+ 0. **Detect versions.** Scaffold with `@latest` if the folder is empty, then read the installed `package.json` and record the majors on the `Stack:` line. When unsure of an API, check the installed package's types, `npm view <pkg> version` and the official docs for that major. **When the skill's code disagrees with the installed version's types or docs, the installed version wins:** adapt the code and log the adaptation under LOG in STRUQ.md. Strip the scaffold's starter fonts, page and CSS. The known shifts and how to tell which side a project is on: [references/setup.md](references/setup.md) §0.
212
+ 1. **Direction.** Run [references/direction.md](references/direction.md) phases 0 to 4: intake (the brief extracted silently, stack detected and recorded as `Stack:`), brand ([references/brand.md](references/brand.md) when there is none), concept, system, and the site plan via [references/brief-router.md](references/brief-router.md) §2. No usable brief? The router's wizard asks at most three plain-language rounds; that is the only early stop. Write `STRUQ.md`, then send the one gate message: summary, materials request, "Veto anything?" Vite? Every later step translates through [references/stack-targets.md](references/stack-targets.md). Redesigning? First run the asset audit in [references/performance.md](references/performance.md) §2 and read [references/triage.md](references/triage.md).
213
+ 2. **Brand files and tokens.** Write the logo, mark and favicon set ([references/brand.md](references/brand.md) §5). Set the root-rem knob, the type system ([references/typography.md](references/typography.md)) and the palette tokens exactly as STRUQ.md records them.
214
+ 3. **Structure and SEO.** Routes, metadata, sitemap, robots and JSON-LD from the PLAN's page list ([references/seo.md](references/seo.md)), before any section is styled. Then, with the routes in place, the OG image per shared route ([references/seo.md](references/seo.md) §3).
215
+ 4. **Wire providers.** Lenis on the GSAP ticker with the reduced-motion guard ([references/setup.md](references/setup.md)). For multi-page sites, the transition provider ([references/route-transitions.md](references/route-transitions.md)) and optionally a loader ([references/loaders.md](references/loaders.md)).
216
+ 5. **Static layout.** Authored in rem, designed at 1440 and 390 at the same time, and checked at 768 wherever a breakpoint changes the column count ([references/mobile-composition.md](references/mobile-composition.md) §4). Type, spacing and surfaces right **before any motion**; motion cannot rescue a flat layout. Nav per [references/nav-header.md](references/nav-header.md), icons per [references/iconography.md](references/iconography.md).
217
+ 6. **Motion in layers.** Reveals, then composition, then the one or two showpieces, then text and SVG choreography, then hover and pointer. WebGL last, as a lazy island with a poster. Read [references/pitfalls.md](references/pitfalls.md) before the first tween.
218
+ 7. **Optimize and sweep.** Every asset through the budgets in [references/performance.md](references/performance.md); then [references/anti-patterns.md](references/anti-patterns.md) as a checklist and the [references/seo.md](references/seo.md) checklist.
219
+ 8. **Verify.** Against a production server only: `next build && next start` (or `vite build && vite preview`), never the dev server, and confirm the port you shoot is owned by that production process (a leftover dev server on the same port answers silently). Then:
220
+ - **Layout:** screenshot at **390, 768, 1440 and 1920** with whatever browser tool the harness has (Playwright or similar). Full-page shots do not show a fixed footer-reveal (the viewport becomes the document height); verify the close from the scroll pass.
221
+ - **Scans:** the 30-second tell scan ([references/anti-patterns.md](references/anti-patterns.md)) and the 60-second feel audit ([references/feel-layer.md](references/feel-layer.md) §5) against the screenshots, not the code.
222
+ - **Motion:** scroll the page end to end with motion on: no dead wheel, no flash on route change, and **every pinned section** engaged and released at a slow and a fast scroll with no jump or snap. Run the descender test ("Gyp, jq." in every masked component at 390 and 1440) and the pin checks in [references/pitfalls.md](references/pitfalls.md) §1 and §2.
223
+ - **No empty regions:** ambitious motion loses to plain pages when a reveal leaves a region blank. Capture a full page with reduced motion, a full page with JavaScript off, and the scroll sequence after each reveal settles; any blank footer, frame, CTA or row that should hold content fails verify ([references/pitfalls.md](references/pitfalls.md) §3.6, §3.7).
224
+ - **Timing:** static PNGs cannot show timing bugs. Take a frame trace of the first 3 seconds (a screenshot every 200 to 250ms from navigation) and confirm each entrance does what its recipe says: the hold frame of a match cut is the photo, a split hero is never shown unsplit, nothing stays at opacity 0.
225
+ - **Vitals:** LCP (with the element it names) and CLS from an in-page `PerformanceObserver` probe under 4x CPU and a throttled network, plus INP from field data or a manual interaction trace, and the first-load JS measured from the served HTML (Turbopack prints no table). Port check, probe, trace and first-load scripts: [references/performance.md](references/performance.md) §8.
226
+ Fix, re-shoot, and write the score under VERIFY in STRUQ.md.
227
+
228
+ ## Audit
229
+
230
+ `/struq audit` changes nothing. It returns:
231
+
232
+ 1. **Score out of 100**, starting at 100: minus 5 per blocklist tell found, minus 3 per baseline violation (type, spacing, surface, color, motion, composition), minus 10 if the page fails to scroll smoothly or a pin jerks, minus 10 if there is no designed mobile layout, minus 5 per asset over budget ([references/performance.md](references/performance.md) §1, capped at minus 15), minus 5 if social images, the sitemap or page metadata are missing ([references/seo.md](references/seo.md)).
233
+ 2. **The tells**, grouped by category, each with the file and line (or screenshot region) and the one-line fix.
234
+ 3. **Feel audit** result from [references/feel-layer.md](references/feel-layer.md) §5.
235
+ 4. **The three changes with the most visual payoff**, ordered by the leverage ranking above.
236
+
237
+ Keep the score honest. An award-tier page lands at 90+; a typical AI-generated page lands under 50.