sheleg-design-skill 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # SHELEG Design — agent skill
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/sheleg-design-skill)](https://www.npmjs.com/package/sheleg-design-skill)
4
+ [![CI](https://github.com/ssheleg/sheleg-design-skill/actions/workflows/validate.yml/badge.svg)](https://github.com/ssheleg/sheleg-design-skill/actions/workflows/validate.yml)
5
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
6
+
3
7
  > A motion + particle interface methodology for building cinematic,
4
8
  > scroll-driven landing pages — packaged as an installable agent skill for
5
9
  > Cursor and Claude.
@@ -60,17 +64,39 @@ npx sheleg-design-skill --force
60
64
  npx sheleg-design-skill --help
61
65
  ```
62
66
 
67
+ ### Other install paths
68
+
69
+ ```bash
70
+ # Claude Code plugin (adds the /sheleg-design command too)
71
+ /plugin marketplace add ssheleg/sheleg-design-skill
72
+ /plugin install sheleg-design@sheleg-design-skill
73
+
74
+ # vercel-labs skills CLI (70+ agents)
75
+ npx skills add ssheleg/sheleg-design-skill
76
+
77
+ # POSIX fallback, no Node
78
+ curl -fsSL https://raw.githubusercontent.com/ssheleg/sheleg-design-skill/main/install.sh | sh
79
+ ```
80
+
63
81
  ### What gets installed
64
82
 
65
83
  | File | Purpose |
66
84
  |---|---|
67
85
  | `SKILL.md` | Agent-facing skill: discovery trigger, the principles, how to apply them, quick-reference rules, common mistakes |
68
86
  | `SHELEG_DESIGN.md` | The full reference: architecture, layer-by-layer mechanics with code, the exact morph math, the DOM↔WebGL projection bridge, a build-from-scratch recipe, and the "why it works" |
87
+ | `styles/*.md` | Style packs — the visual identity layer: `instrument-console` (near-black console, electric-blue signal) and `editorial-luxury` (warm cream/espresso/sage, dossier motifs). Each locks palette, type, texture, motion tokens, motifs, and bans; the pack contract lets you author new styles |
69
88
 
70
89
  After installing, a Cursor or Claude agent in that project can discover the
71
90
  skill and use it when you ask it to build or upgrade a cinematic,
72
91
  scroll-driven, particle-backed page.
73
92
 
93
+ ## Style-agnostic motion, pluggable identity
94
+
95
+ The motion methodology (one clock, layered responses, degrade-to-calm) is
96
+ independent of the visual style. The look comes from a **style pack** the
97
+ agent picks per project — dark instrument console or warm editorial luxury
98
+ out of the box, or a new pack authored against the same contract.
99
+
74
100
  ## Stack-agnostic
75
101
 
76
102
  The skill teaches **principles and architecture**, not a fixed dependency set.
@@ -83,6 +109,14 @@ applies to any stack that can render to a canvas/WebGL surface and read scroll.
83
109
  The installer is a single zero-dependency Node script, so `npx` runs instantly
84
110
  with no install step and no supply-chain surface.
85
111
 
112
+ ## Development
113
+
114
+ `python3 test/validate.py` checks repo consistency (manifests, version sync,
115
+ skill/command/rule front-matter, relative links); CI runs it plus a CLI smoke
116
+ test on every push and PR. Versioning is semver; bump `marketplace.json` +
117
+ `plugin.json` + `package.json` + `CHANGELOG.md` together — the validator
118
+ enforces the sync.
119
+
86
120
  ## License
87
121
 
88
122
  MIT © ssheleg
package/bin/cli.js CHANGED
@@ -14,9 +14,21 @@
14
14
  const fs = require("fs");
15
15
  const path = require("path");
16
16
 
17
- const SKILL_DIR = path.join(__dirname, "..", "skill");
17
+ const SKILL_DIR = path.join(
18
+ __dirname,
19
+ "..",
20
+ "plugins",
21
+ "sheleg-design",
22
+ "skills",
23
+ "sheleg-design",
24
+ );
18
25
  const SKILL_SLUG = "sheleg-design";
19
- const FILES = ["SKILL.md", "SHELEG_DESIGN.md"];
26
+ const FILES = [
27
+ "SKILL.md",
28
+ "SHELEG_DESIGN.md",
29
+ "styles/instrument-console.md",
30
+ "styles/editorial-luxury.md",
31
+ ];
20
32
 
21
33
  const pkg = require(path.join(__dirname, "..", "package.json"));
22
34
 
@@ -79,6 +91,7 @@ ${c("bold", "Default")}
79
91
  ${c("bold", "What it installs")}
80
92
  SKILL.md the agent-facing skill (discovery + principles)
81
93
  SHELEG_DESIGN.md the full reference (architecture, recipes, why it works)
94
+ styles/*.md style packs (instrument-console, editorial-luxury)
82
95
  `);
83
96
  }
84
97
 
@@ -135,14 +148,17 @@ function main() {
135
148
 
136
149
  fs.mkdirSync(targetDir, { recursive: true });
137
150
  for (const f of FILES) {
138
- fs.copyFileSync(path.join(SKILL_DIR, f), path.join(targetDir, f));
151
+ const dest = path.join(targetDir, f);
152
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
153
+ fs.copyFileSync(path.join(SKILL_DIR, f), dest);
139
154
  }
140
155
 
141
156
  const rel = path.relative(cwd, targetDir) || ".";
142
157
  console.log(
143
158
  `\n${c("green", "✓")} ${c("bold", "SHELEG Design")} installed to ${c("blue", rel + "/")}\n` +
144
159
  ` ${c("dim", "SKILL.md")} the agent skill\n` +
145
- ` ${c("dim", "SHELEG_DESIGN.md")} the full reference\n\n` +
160
+ ` ${c("dim", "SHELEG_DESIGN.md")} the full reference\n` +
161
+ ` ${c("dim", "styles/")} style packs (console / editorial)\n\n` +
146
162
  `Your Cursor / Claude agent can now discover the skill and build\n` +
147
163
  `cinematic, scroll-driven, particle-backed pages on its principles.\n\n` +
148
164
  `${c("dim", "Docs: " + pkg.homepage)}\n`,
@@ -0,0 +1,50 @@
1
+ ---
2
+ description: Apply SHELEG Design when building or upgrading a cinematic scroll-driven landing page, hero, or particle/WebGL scroll experience — or when scroll motion feels busy, janky, or out of sync
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # SHELEG Design — single-clock scroll motion
7
+
8
+ A page feels cinematic from a **single source of truth** (measured scroll
9
+ position) driving **many cheap, layered, independently-degradable responses** —
10
+ not from many animations. If the project has the full skill installed
11
+ (`.cursor/skills/sheleg-design/`), read its `SKILL.md`, `SHELEG_DESIGN.md`,
12
+ and the chosen style pack in `styles/` (instrument-console — dark console;
13
+ editorial-luxury — warm editorial) first; otherwise follow the contract
14
+ below (self-contained on purpose).
15
+
16
+ ## Five principles, in order
17
+
18
+ 1. **One clock.** All motion derives from one measured scroll state in one
19
+ store; no layer measures scroll itself, so layers never drift out of phase.
20
+ 2. **Read per frame, notify rarely.** Hot consumers (WebGL/canvas/progress
21
+ rail) read the store imperatively with zero framework renders; only coarse
22
+ act/section changes notify the framework.
23
+ 3. **Hold, then redeploy.** Hold a formation ~80% of a section, then morph in
24
+ a short, phase-staggered, arc-curved wave. Crossfades between point sets
25
+ are banned.
26
+ 4. **Earned motion.** Scrub only for instruments that narrate state over time
27
+ (charts, step flows); hover/entrance motion stays sub-500ms and never
28
+ gates content visibility.
29
+ 5. **Degrade to calm.** `prefers-reduced-motion` / coarse pointer / no-WebGL
30
+ collapse to a static, fully-legible page. The effect is a bonus, never a
31
+ dependency.
32
+
33
+ ## Non-negotiables
34
+
35
+ - One scroll store, two read paths: live getter for per-frame readers,
36
+ coarse subscription for framework-rendered UI.
37
+ - Storyboard in data: a `SCENES` registry (`{ anchor, formation, focusX,
38
+ energy }` per section); iterate on data before render loops.
39
+ - Smooth scroll (e.g. Lenis) driven from the animation library's ticker so
40
+ scrubbed instruments and the particle field share one inertia.
41
+ - Lazy-load GSAP/WebGL out of the initial bundle; mount WebGL one frame
42
+ after hydration paints.
43
+ - One ease + one small duration/stagger token set site-wide.
44
+ - Scrubbed SVG: `ease: 'none'`, `pathLength={1}`, always kill timelines and
45
+ triggers on cleanup.
46
+ - Animate only `transform` and `opacity`.
47
+ - Every layer ships its reduced-motion/fallback branch in the same commit.
48
+ - At most one parallax figure per viewport; no scrub on hero/entrances.
49
+ - Visual system (color, type, spacing, components) is finished before any
50
+ motion work starts.
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "sheleg-design-skill",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "SHELEG Design — an agent skill for building cinematic, scroll-driven, particle-backed landing pages. Installs a SKILL.md + reference doc into your project so Cursor/Claude agents can build sites with a single-clock motion system, a scene-formation particle engine, and degrade-to-calm fallbacks.",
5
5
  "bin": {
6
6
  "sheleg-design-skill": "bin/cli.js"
7
7
  },
8
8
  "files": [
9
9
  "bin/",
10
- "skill/",
10
+ "plugins/",
11
+ "cursor/",
11
12
  "README.md",
12
13
  "LICENSE"
13
14
  ],
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "sheleg-design",
3
+ "description": "SHELEG Design methodology for cinematic scroll-driven landing pages: a single scroll clock driving layered, degrade-to-calm motion (WebGL particle formations, 2D fallback, parallax, scrubbed instruments, progress rail). Ships the sheleg-design skill, the full architecture reference, and the /sheleg-design command.",
4
+ "version": "0.4.0",
5
+ "author": {
6
+ "name": "ssheleg"
7
+ },
8
+ "homepage": "https://github.com/ssheleg/sheleg-design-skill",
9
+ "repository": "https://github.com/ssheleg/sheleg-design-skill",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "design",
13
+ "motion",
14
+ "animation",
15
+ "particles",
16
+ "webgl",
17
+ "scroll",
18
+ "landing-page",
19
+ "claude-code"
20
+ ]
21
+ }
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: Apply the SHELEG Design methodology (single-clock, layered, degrade-to-calm motion) to the current landing-page / hero / scroll-animation task
3
+ argument-hint: [what to build or upgrade]
4
+ ---
5
+
6
+ Invoke the `sheleg-design` skill and apply it to the current request.
7
+
8
+ - Read the skill's SKILL.md, then its full reference `SHELEG_DESIGN.md`
9
+ (same directory) before designing anything.
10
+ - Task: $ARGUMENTS — if empty, ask what page or section to build/upgrade,
11
+ then proceed per the skill's "How to Apply" order.
12
+ - Follow the skill's non-negotiables (Quick Reference table) and ship every
13
+ layer's reduced-motion/fallback branch in the same commit.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sheleg-design
3
- description: Use when building or upgrading a cinematic scroll-driven landing page, marketing site, or hero experience — a particle/WebGL background, scroll-linked animation, parallax, pinned or scrubbed sections, formation-morphing scenes — or when such a page feels busy or janky, or its motion layers drift out of sync with each other. Read before designing the motion architecture; pairs with an existing visual system, does not replace one.
3
+ description: Use when building or upgrading a cinematic scroll-driven landing page, marketing site, or hero experience — a particle/WebGL background, scroll-linked animation, parallax, pinned or scrubbed sections, formation-morphing scenes — or when such a page feels busy or janky, or its motion layers drift out of sync. RU triggers - кинематографичный лендинг, скролл-анимация, лендинг с частицами. Pairs with an existing visual system, does not replace one.
4
4
  ---
5
5
 
6
6
  # SHELEG Design
@@ -41,9 +41,26 @@ system or copy isn't finished yet. Fix those first; motion amplifies weakness.
41
41
  5. **Degrade to calm.** Reduced-motion / coarse pointer / no-WebGL collapse to
42
42
  a static, fully-legible page. The effect is a bonus, never a dependency.
43
43
 
44
+ ## Style packs
45
+
46
+ The motion methodology is style-agnostic; the visual identity comes from a
47
+ style pack in [`styles/`](./styles/):
48
+
49
+ | Pack | Look | Choose for |
50
+ |---|---|---|
51
+ | [`instrument-console`](./styles/instrument-console.md) | near-black aerospace console, one electric-blue signal, mono telemetry | technical / systems / infra products |
52
+ | [`editorial-luxury`](./styles/editorial-luxury.md) | warm cream + espresso ink, sage accent, Fraunces/Newsreader, dossier motifs | editorial / research / premium B2B |
53
+
54
+ Read the chosen pack in full before styling anything — it supplies the
55
+ palette, type, texture, motion-token values, signature motifs, and bans.
56
+ For a new style, author a new pack file with the same headings (Register /
57
+ Palette / Type / Texture & surface / Motion tokens / Signature motifs /
58
+ Micro-interactions / Bans); never invent token values ad hoc.
59
+
44
60
  ## How to Apply
45
61
 
46
- 1. Visual system first (color, type, spacing, components).
62
+ 1. Visual system first: pick (or author) a style pack, apply its tokens as
63
+ the site-wide design tokens (color, type, spacing, components).
47
64
  2. Build bottom-up in the §11 layer order: scroll clock → smooth scroll →
48
65
  particle field → 2D fallback → DOM choreography → reveals → scrubbed
49
66
  instruments → optional DOM↔WebGL bridge. One small file per layer.
@@ -0,0 +1,99 @@
1
+ # Style pack — Editorial Luxury
2
+
3
+ Origin: prowl.chat (production). Warm cream field, espresso ink, one
4
+ functional sage-green accent, terracotta as a rare editorial highlight,
5
+ classified-red reserved for negatives only. "Design *is* the product" on
6
+ public pages; quiet and fast inside the app. Dossier/editorial DNA:
7
+ hairline rules, eyebrow labels, stamp/seal motifs, mono data, authored
8
+ artifact previews instead of icon cards.
9
+
10
+ ## Register
11
+
12
+ Choose this pack for warm, editorial, print-inspired products: research /
13
+ intelligence tools, content products, premium B2B. Two registers:
14
+ **brand** (landing, use-case, legal, shared pages — cinematic, editorial)
15
+ and **product** (authenticated app — quiet micro-interactions only, never
16
+ cinematic noise).
17
+
18
+ ## Palette
19
+
20
+ | Token | Value | Role |
21
+ |---|---|---|
22
+ | `--paper` | `#fbf6ec` | primary cream field |
23
+ | `--paper-2` | `#f3ead9` | raised card on cream |
24
+ | `--paper-3` | `#ece0cb` | deeper inset |
25
+ | `--espresso` | `#1b150e` | dark section field |
26
+ | `--espresso-2` | `#241c12` | raised card on espresso |
27
+ | `--cream` | `#f4ecdc` | ink on espresso |
28
+ | `--ink` / `-soft` / `-faint` | `#241c14` / `#5b4f3d` / `#8a7c64` | text ramp on cream |
29
+ | `--accent` (sage) / `-deep` | `#3f7d5f` / `#2f5e47` | THE single functional accent (links, CTA, "signal") |
30
+ | `--terra` | `#b5623f` | rare editorial highlight only |
31
+ | `--red` | `#a83a2b` | negatives ONLY (comparison "without") |
32
+ | `--hair` / `-strong` | `rgba(36,28,20,0.13)` / `rgba(36,28,20,0.22)` | hairline rules |
33
+
34
+ Contrast: body on cream must clear 4.5:1 — `--ink` / `--ink-soft`, never
35
+ `--ink-faint` for sustained reading. On espresso, text is `--cream` and the
36
+ sage accent brightens to `#9fd9bc`.
37
+
38
+ ## Type
39
+
40
+ - Display: **Fraunces** — oversized, optical, tracked `-0.02…-0.03em`, hero
41
+ ceiling ~7rem via clamp.
42
+ - Body: **Newsreader** — relaxed, measure ≤66ch.
43
+ - Mono: **JetBrains Mono** — eyebrows, labels, numbers, code, "signal" tags.
44
+ - Three families, no more; hierarchy through scale + weight.
45
+
46
+ ## Texture & surface
47
+
48
+ - Fixed warm radial field + ~4% film-grain multiply overlay (cheap, no blur).
49
+ - Squircle radii 14 / 22 / 30px; double-bezel cards
50
+ (`inset 0 1px 0 rgba(255,251,242,0.7)` highlight).
51
+ - Soft ambient elevation only (`0 18px 50px -28px rgba(36,28,20,0.30)`
52
+ scale); no harsh dark drops, no outer glows on buttons.
53
+
54
+ ## Motion tokens
55
+
56
+ - Ease `cubic-bezier(0.22, 1, 0.36, 1)` (ease-out-expo feel) — the one
57
+ site-wide curve for the SHELEG token set.
58
+ - Spring `cubic-bezier(0.32, 0.72, 0, 1)` — press/magnetic feedback only.
59
+ - Base duration `0.7s` for brand-register reveals; product register stays in
60
+ the SHELEG fast/base range (≤0.32s).
61
+
62
+ ## Signature motifs
63
+
64
+ - A recurring sage **"signal"** motif travels the narrative: raw inputs →
65
+ pipeline → synthesized deliverable. One story, one color.
66
+ - **Authored artifact previews** instead of flat icon cards: composed mock
67
+ report/infographic/PDF/PPTX/video frames that assemble on scroll.
68
+ - Dossier primitives: `.dossier-card`, hairline `.rule`, `.eyebrow`,
69
+ `.stamp`, `.data-table`, tabular-nums data, footnote captions.
70
+ - Visualization rule (key contract): all data-viz animation is **CSS-driven
71
+ off a `.revealed` ancestor** (bars `scaleX/Y`, sparklines/donuts via
72
+ `stroke-dashoffset`) — identical in plain and cinematic reveal paths,
73
+ correct static final state under reduced-motion, **fail-open** if JS/CDN
74
+ dies (content always visible).
75
+
76
+ ## Micro-interactions
77
+
78
+ - Buttons: tactile `translateY(-2px)` + spring; no glow.
79
+ - Magnetic primary CTAs and a small sage cursor-ring accent — desktop +
80
+ fine pointer only, gated with the SHELEG degrade rules.
81
+ - Focus-visible: 2px sage outline, offset 3px (brightened on espresso).
82
+ - Cards lift + border warms on hover; never nested-card-in-card.
83
+
84
+ ## Bans
85
+
86
+ - No gradient text, no side-stripe accent borders, no glassmorphism, no
87
+ neon/outer-glow shadows, no purple.
88
+ - No emojis in product UI; no Inter/system display fonts (Fraunces owns
89
+ display).
90
+ - Never flatten the cream identity into generic white; never let motion
91
+ gate content visibility.
92
+
93
+ ## Gotchas
94
+
95
+ - Token-first re-skin: override the token layer in `:root`, map legacy
96
+ token names onto it — do NOT restyle components one by one.
97
+ - After any theme/token migration, **sweep hardcoded literals** (hex/rgba
98
+ left in CSS/JS keep the old palette and read as inverted on the new
99
+ theme).
@@ -0,0 +1,89 @@
1
+ # Style pack — Instrument Console
2
+
3
+ Origin: Nicegram Business OS landing (the SHELEG reference implementation).
4
+ A near-black aerospace console: deep layered surfaces, hairline seams, one
5
+ electric-blue signal accent, mono telemetry labels. The particle field and
6
+ every instrument read as one precision device responding to the hand.
7
+
8
+ ## Register
9
+
10
+ Choose this pack for technical, systems, infra, or "operating system"
11
+ products where the aesthetic is calm precision hardware. Single dark
12
+ register across landing and app; brightness (energy) varies per scene, hue
13
+ does not.
14
+
15
+ ## Palette
16
+
17
+ | Token | Value | Role |
18
+ |---|---|---|
19
+ | `--base` | `#05070a` | page field (near-black) |
20
+ | `--surface-1/2/3` | `#0a0e14` / `#10151d` / `#161c26` | ascending raised panels |
21
+ | `--hairline` / `-strong` | `#1e2630` / `#2b3542` | panel seams, rules |
22
+ | `--ink` | `#eef2f7` | primary text |
23
+ | `--ink-muted` / `-faint` | `#9aa7b6` / `#5f6b7a` | secondary / captions |
24
+ | `--accent` | `#3392ff` | THE electric-blue signal (CTA, links, particles) |
25
+ | `--accent-dim` / `-bright` | `#1f5fb0` / `#6bb3ff` | pressed / highlighted signal |
26
+ | `--accent-glow` | `rgba(51,146,255,0.18)` | the only permitted glow |
27
+ | `--ok` / `--warn` | `#46d39a` / `#e0a030` | status semantics only |
28
+
29
+ The particle field, progress rail, and all instruments are tinted with
30
+ `--accent` only — energy per scene changes brightness, never hue.
31
+
32
+ ## Type
33
+
34
+ - Display + body: **Geist Sans** (or an equivalent neutral grotesk) —
35
+ weight 600 headlines, clamp-scaled (hero ~5.25rem ceiling), tight but
36
+ not tracked-negative.
37
+ - Data: **Geist Mono** (or ui-monospace) — telemetry eyebrows, numeric
38
+ readouts, section indices ("02 / CONTROL"), code.
39
+ - Two families; mono is a signature, not a garnish — every label that
40
+ narrates system state is mono.
41
+
42
+ ## Texture & surface
43
+
44
+ - Flat panels separated by 1px hairlines; radii 4 / 8 / 14px (+ pill) —
45
+ machined, not squircle.
46
+ - Elevation via surface steps (`--surface-1..3`), not shadows; the single
47
+ glow `0 0 0 1px rgba(51,146,255,0.4), 0 8px 30px rgba(51,146,255,0.18)`
48
+ is reserved for the active signal element.
49
+ - No grain, no blur; darkness itself is the texture.
50
+
51
+ ## Motion tokens
52
+
53
+ - Ease `cubic-bezier(0.16, 1, 0.3, 1)` — the one site-wide curve
54
+ (the SHELEG default token set: 0.18 / 0.32 / 0.55 / 0.8s, stagger 0.07).
55
+ - Section rhythm `clamp(9rem, 24vh, 20rem)` vertical padding.
56
+
57
+ ## Signature motifs
58
+
59
+ - WebGL particle formations as the narrative backdrop (SCENES registry),
60
+ electric-blue, hold-then-redeploy per the SHELEG core.
61
+ - Right-edge progress rail with act markers; nav act badge ("02 /
62
+ CONTROL") driven by the coarse store subscription.
63
+ - Frame/HUD chrome: thin viewport frame, corner ticks, scan/dim of
64
+ off-band sections (attention spotlight).
65
+ - Scrubbed SVG instruments (charts, step flows) drawn hairline-thin with
66
+ mono annotations.
67
+
68
+ ## Micro-interactions
69
+
70
+ - Buttons: surface-step + accent fill on primary; press = 1 shade dimmer
71
+ (`--accent-dim`), no bounce.
72
+ - Reveal primitives themed per act: Scatter (drift+blur resolve), Lock
73
+ (snap into slot), Clip (mechanical wipe), Pulse (lock acquired).
74
+ - Focus-visible: 1px `--accent` ring + `--accent-glow` halo.
75
+
76
+ ## Bans
77
+
78
+ - One accent hue — no second color except `--ok`/`--warn` status semantics.
79
+ - No gradient text, no glassmorphism/backdrop blur, no purple/neon
80
+ rainbow, no colored shadows besides `--accent-glow`.
81
+ - No light sections — contrast comes from surface steps, not inversion.
82
+ - No decorative serif/display fonts; the console voice is grotesk + mono.
83
+
84
+ ## Gotchas
85
+
86
+ - Glow discipline: `--accent-glow` on more than one element per viewport
87
+ destroys the "single signal" read — the page becomes a christmas tree.
88
+ - Dark UIs hide low-contrast text: `--ink-faint` is for captions only,
89
+ never sustained reading (fails 4.5:1 on `--surface-1`).