bigpowers 2.4.0 → 2.4.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 (85) hide show
  1. package/.pi/package.json +1 -1
  2. package/.pi/prompts/compose-workflow.md +1 -1
  3. package/.pi/prompts/diagnose-root.md +1 -1
  4. package/.pi/prompts/evolve-skill.md +1 -1
  5. package/.pi/prompts/grill-with-docs.md +1 -1
  6. package/.pi/prompts/research-first.md +1 -1
  7. package/.pi/prompts/reset-baseline.md +1 -1
  8. package/.pi/prompts/run-evals.md +1 -1
  9. package/.pi/prompts/scope-work.md +1 -1
  10. package/.pi/prompts/search-skills.md +1 -1
  11. package/.pi/prompts/setup-environment.md +1 -1
  12. package/.pi/prompts/simulate-agents.md +1 -1
  13. package/.pi/prompts/slice-tasks.md +1 -1
  14. package/.pi/prompts/stocktake-skills.md +1 -1
  15. package/.pi/prompts/verify-work.md +1 -1
  16. package/.pi/skills/assess-impact/SKILL.md +2 -1
  17. package/.pi/skills/audit-code/SKILL.md +1 -0
  18. package/.pi/skills/build-epic/SKILL.md +1 -0
  19. package/.pi/skills/change-request/SKILL.md +1 -0
  20. package/.pi/skills/commit-message/SKILL.md +1 -0
  21. package/.pi/skills/compose-workflow/SKILL.md +2 -1
  22. package/.pi/skills/craft-skill/SKILL.md +1 -0
  23. package/.pi/skills/deepen-architecture/SKILL.md +1 -0
  24. package/.pi/skills/define-language/SKILL.md +2 -1
  25. package/.pi/skills/define-success/SKILL.md +2 -1
  26. package/.pi/skills/delegate-task/SKILL.md +1 -0
  27. package/.pi/skills/design-interface/SKILL.md +2 -1
  28. package/.pi/skills/develop-tdd/SKILL.md +1 -0
  29. package/.pi/skills/diagnose-root/SKILL.md +2 -1
  30. package/.pi/skills/dispatch-agents/SKILL.md +1 -0
  31. package/.pi/skills/edit-document/SKILL.md +1 -0
  32. package/.pi/skills/elaborate-spec/SKILL.md +2 -1
  33. package/.pi/skills/enforce-first/SKILL.md +2 -1
  34. package/.pi/skills/evolve-skill/SKILL.md +2 -1
  35. package/.pi/skills/execute-plan/SKILL.md +1 -0
  36. package/.pi/skills/fix-bug/SKILL.md +1 -0
  37. package/.pi/skills/grill-me/SKILL.md +2 -1
  38. package/.pi/skills/grill-with-docs/SKILL.md +2 -1
  39. package/.pi/skills/guard-git/SKILL.md +1 -0
  40. package/.pi/skills/hook-commits/SKILL.md +1 -0
  41. package/.pi/skills/inspect-quality/SKILL.md +2 -1
  42. package/.pi/skills/investigate-bug/SKILL.md +2 -1
  43. package/.pi/skills/kickoff-branch/SKILL.md +2 -1
  44. package/.pi/skills/map-codebase/SKILL.md +2 -1
  45. package/.pi/skills/migrate-spec/SKILL.md +1 -0
  46. package/.pi/skills/model-domain/SKILL.md +1 -0
  47. package/.pi/skills/orchestrate-project/SKILL.md +1 -0
  48. package/.pi/skills/organize-workspace/SKILL.md +2 -1
  49. package/.pi/skills/plan-refactor/SKILL.md +1 -0
  50. package/.pi/skills/plan-release/SKILL.md +2 -1
  51. package/.pi/skills/plan-work/SKILL.md +2 -1
  52. package/.pi/skills/release-branch/SKILL.md +2 -1
  53. package/.pi/skills/request-review/SKILL.md +1 -0
  54. package/.pi/skills/research-first/SKILL.md +2 -1
  55. package/.pi/skills/reset-baseline/SKILL.md +2 -1
  56. package/.pi/skills/respond-review/SKILL.md +1 -0
  57. package/.pi/skills/run-evals/SKILL.md +2 -1
  58. package/.pi/skills/run-planning/SKILL.md +2 -1
  59. package/.pi/skills/scope-work/SKILL.md +2 -1
  60. package/.pi/skills/search-skills/SKILL.md +2 -1
  61. package/.pi/skills/seed-conventions/SKILL.md +1 -0
  62. package/.pi/skills/session-state/SKILL.md +1 -0
  63. package/.pi/skills/setup-environment/SKILL.md +2 -1
  64. package/.pi/skills/simulate-agents/SKILL.md +2 -1
  65. package/.pi/skills/slice-tasks/SKILL.md +2 -1
  66. package/.pi/skills/spike-prototype/SKILL.md +2 -1
  67. package/.pi/skills/stocktake-skills/SKILL.md +2 -1
  68. package/.pi/skills/survey-context/SKILL.md +1 -0
  69. package/.pi/skills/terse-mode/SKILL.md +2 -1
  70. package/.pi/skills/trace-requirement/SKILL.md +2 -1
  71. package/.pi/skills/using-bigpowers/SKILL.md +2 -1
  72. package/.pi/skills/validate-fix/SKILL.md +2 -1
  73. package/.pi/skills/verify-work/SKILL.md +2 -1
  74. package/.pi/skills/visual-dashboard/SKILL.md +1 -0
  75. package/.pi/skills/wire-observability/SKILL.md +1 -0
  76. package/.pi/skills/write-document/SKILL.md +1 -0
  77. package/CHANGELOG.md +7 -0
  78. package/SKILL-INDEX.md +34 -33
  79. package/dashboard/src/web/client.html +191 -249
  80. package/package.json +1 -1
  81. package/scripts/generate-reference-tables.sh +1 -1
  82. package/scripts/sync-skills.sh +22 -10
  83. package/scripts/validate-skill-yaml.py +73 -0
  84. package/visual-dashboard/scripts/cockpit.html +123 -16
  85. package/visual-dashboard/scripts/frame-template.html +181 -45
@@ -31,13 +31,17 @@ for skill_dir in "$REPO_ROOT"/*/; do
31
31
  skill_md="$skill_dir/SKILL.md"
32
32
  [[ -f "$skill_md" ]] || continue
33
33
 
34
- # Extract name and description from YAML frontmatter
34
+ # Extract name, model, and description from YAML frontmatter
35
35
  name=$(awk '/^---/{f++} f==1 && /^name:/{print; exit}' "$skill_md" | sed 's/^name:[[:space:]]*//')
36
- description=$(awk '/^---/{f++} f==1 && /^description:/{p=1} p && !/^---/{print} f==2{exit}' "$skill_md" \
37
- | sed 's/^description:[[:space:]]*//' \
36
+ model=$(awk '/^---/{f++} f==1 && /^model:/{print; exit}' "$skill_md" | sed 's/^model:[[:space:]]*//')
37
+ # Capture description: from "description:" line to next YAML key or closing ---
38
+ description=$(awk '/^---/{f++; next} f==1 && /^description:/{p=1; sub(/^description:[[:space:]]*/,""); print; next} f==1 && p && /^[a-z]+:/{exit} f==1 && p{print}' "$skill_md" \
38
39
  | tr -d '\n' \
39
40
  | sed -E 's/[[:space:]]+/ /g')
40
41
 
42
+ # Escape double quotes and backslashes for safe double-quoted YAML output
43
+ description_escaped=$(echo "$description" | sed 's/\\/\\\\/g; s/"/\\"/g')
44
+
41
45
  [[ -z "$name" ]] && continue
42
46
 
43
47
  # Build concatenated content: SKILL.md body + all other *.md files alphabetically
@@ -55,7 +59,7 @@ for skill_dir in "$REPO_ROOT"/*/; do
55
59
  cursor_file="$CURSOR_RULES/$name.mdc"
56
60
  {
57
61
  echo "---"
58
- echo "description: \"$description\""
62
+ echo "description: \"$description_escaped\""
59
63
  echo "alwaysApply: false"
60
64
  echo "---"
61
65
  echo ""
@@ -67,7 +71,7 @@ for skill_dir in "$REPO_ROOT"/*/; do
67
71
  {
68
72
  echo "---"
69
73
  echo "name: $name"
70
- echo "description: \"$description\""
74
+ echo "description: \"$description_escaped\""
71
75
  echo "---"
72
76
  echo ""
73
77
  echo "$body"
@@ -79,11 +83,8 @@ for skill_dir in "$REPO_ROOT"/*/; do
79
83
  prompt_file="commands/prompts/$name.md"
80
84
  echo "$body" > "$GEMINI_EXT_DIR/$prompt_file"
81
85
 
82
- # Escape double quotes and backslashes for TOML
83
- description_toml=$(echo "$description" | sed 's/\\/\\\\/g; s/"/\\"/g')
84
-
85
86
  {
86
- echo "description = \"$description_toml\""
87
+ echo "description = \"$description_escaped\""
87
88
  echo "prompt = \"@{$prompt_file}\""
88
89
  } > "$GEMINI_COMMANDS/$name.toml"
89
90
 
@@ -93,7 +94,8 @@ for skill_dir in "$REPO_ROOT"/*/; do
93
94
  {
94
95
  echo "---"
95
96
  echo "name: $name"
96
- echo "description: \"$description\""
97
+ echo "description: \"$description_escaped\""
98
+ [[ -n "$model" ]] && echo "model: $model"
97
99
  echo "---"
98
100
  echo ""
99
101
  echo "$body"
@@ -206,6 +208,16 @@ if [[ -f "$manifest" ]]; then
206
208
  fi
207
209
  fi
208
210
 
211
+ # Regression guard (BUG-2026-06-18T100000): validate generated YAML frontmatter
212
+ validate_script="$REPO_ROOT/scripts/validate-skill-yaml.py"
213
+ if [[ -f "$validate_script" ]] && command -v python3 &>/dev/null; then
214
+ if ! python3 "$validate_script" > /dev/null 2>&1; then
215
+ echo "sync-skills: FAIL — YAML frontmatter validation failed" >&2
216
+ python3 "$validate_script" >&2
217
+ exit 1
218
+ fi
219
+ fi
220
+
209
221
  # Regenerate derived reference tables from live SKILL.md frontmatter
210
222
  bash "$REPO_ROOT/scripts/generate-reference-tables.sh"
211
223
 
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ """Validate YAML frontmatter in all .pi/skills/*/SKILL.md files."""
3
+ import glob
4
+ import sys
5
+ import os
6
+
7
+ try:
8
+ import yaml
9
+ except ImportError:
10
+ print("ERROR: PyYAML required — pip install pyyaml")
11
+ sys.exit(2)
12
+
13
+ SKILLS_DIR = ".pi/skills"
14
+ files = sorted(glob.glob(f"{SKILLS_DIR}/*/SKILL.md"))
15
+
16
+ parse_failures = []
17
+ semantic_failures = []
18
+ passed = []
19
+
20
+ for path in files:
21
+ skill_name = os.path.basename(os.path.dirname(path))
22
+ with open(path) as f:
23
+ content = f.read()
24
+
25
+ parts = content.split("---", 2)
26
+ if len(parts) < 3:
27
+ parse_failures.append((skill_name, "missing frontmatter delimiters"))
28
+ continue
29
+
30
+ frontmatter = parts[1]
31
+
32
+ try:
33
+ data = yaml.safe_load(frontmatter)
34
+ except yaml.YAMLError as e:
35
+ parse_failures.append((skill_name, str(e).split("\n")[0]))
36
+ continue
37
+
38
+ if data is None:
39
+ parse_failures.append((skill_name, "empty frontmatter"))
40
+ continue
41
+
42
+ # Semantic check: model hint embedded in description
43
+ desc = data.get("description", "")
44
+ if desc and "model: sonnet" in desc:
45
+ semantic_failures.append(skill_name)
46
+
47
+ passed.append(skill_name)
48
+
49
+ # Report
50
+ print(f"Total SKILL.md files: {len(files)}")
51
+ print(f"Parse OK: {len(passed)}")
52
+ print(f"Parse FAIL: {len(parse_failures)}")
53
+ print(f"Semantic issues (model in desc): {len(semantic_failures)}")
54
+ print()
55
+
56
+ if parse_failures:
57
+ print("=== PARSE FAILURES ===")
58
+ for name, err in parse_failures:
59
+ print(f" {name}: {err}")
60
+ print()
61
+
62
+ if semantic_failures:
63
+ print("=== SEMANTIC ISSUES (model embedded in description) ===")
64
+ for name in sorted(semantic_failures):
65
+ print(f" {name}")
66
+ print()
67
+
68
+ if parse_failures or semantic_failures:
69
+ print(f"FAIL: {len(parse_failures)} parse + {len(semantic_failures)} semantic issues")
70
+ sys.exit(1)
71
+ else:
72
+ print("PASS: all SKILL.md YAML frontmatter valid")
73
+ sys.exit(0)
@@ -4,27 +4,100 @@
4
4
  <meta charset="utf-8">
5
5
  <title>bigpowers cockpit</title>
6
6
  <style>
7
- * { box-sizing: border-box; }
8
- body { font-family: system-ui, sans-serif; margin: 0; display: grid; grid-template-rows: auto 1fr; height: 100vh; }
9
- header { padding: 1rem 1.5rem; background: #1a1a2e; color: #eee; display: flex; gap: 2rem; flex-wrap: wrap; }
10
- header span { opacity: 0.85; }
11
- main { display: grid; grid-template-columns: 1fr 1fr; gap: 0; min-height: 0; }
12
- section { padding: 1rem 1.5rem; overflow: auto; border-top: 1px solid #ddd; }
13
- section h2 { margin-top: 0; font-size: 1rem; text-transform: uppercase; letter-spacing: 0.05em; color: #555; }
14
- .epic { padding: 0.5rem 0; border-bottom: 1px solid #eee; }
15
- .epic strong { color: #1a1a2e; }
7
+ /* ===== GRID TOKENS ===== */
8
+ :root{
9
+ --cols:12;
10
+ --bl:8px;
11
+ --lh:24px;
12
+ --gutter:24px;
13
+ --margin:72px;
14
+ --pad:96px;
15
+ --maxw:1296px;
16
+ --paper:#ffffff;
17
+ --ink:#111315;
18
+ --ink-soft:#5b6066;
19
+ --accent:#e4002b;
20
+ --g-col:rgba(228,0,43,.075);
21
+ --g-edge:rgba(228,0,43,.40);
22
+ --g-base:rgba(0,150,140,.34);
23
+ --g-base-min:rgba(0,150,140,.12);
24
+ }
25
+ *{box-sizing:border-box;}
26
+ body{margin:0;background:var(--paper);color:var(--ink);
27
+ font-family:"Inter",system-ui,sans-serif;font-size:16px;line-height:var(--lh);
28
+ -webkit-font-smoothing:antialiased;}
29
+
30
+ /* ---- scaffold ---- */
31
+ .spread{position:relative;width:100%;}
32
+ .wrap{position:relative;max-width:var(--maxw);margin:0 auto;padding:var(--lh) var(--margin);}
33
+ .grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);
34
+ column-gap:var(--gutter);row-gap:var(--lh);}
35
+ .band{grid-column:1 / -1;display:grid;grid-template-columns:subgrid;
36
+ column-gap:var(--gutter);row-gap:var(--lh);align-items:start;}
37
+ @supports not (grid-template-columns:subgrid){
38
+ .band{grid-template-columns:repeat(var(--cols),1fr);}
39
+ }
40
+
41
+ /* ---- toggle & guides ---- */
42
+ .guides{position:absolute;inset:0;pointer-events:none;z-index:60;opacity:0;
43
+ transition:opacity .26s ease;}
44
+ body.grid-on .guides{opacity:1;}
45
+ .guides .cols{position:absolute;top:0;bottom:0;left:var(--margin);right:var(--margin);
46
+ display:grid;grid-template-columns:repeat(var(--cols),1fr);column-gap:var(--gutter);}
47
+ .guides .col{background:var(--g-col);
48
+ box-shadow:inset 1px 0 0 var(--g-edge),inset -1px 0 0 var(--g-edge);position:relative;}
49
+ .guides .col span{position:absolute;top:32px;left:0;right:0;text-align:center;
50
+ font-family:"Space Mono",monospace;font-size:10px;line-height:1;color:var(--accent);}
51
+ .guides .rows{position:absolute;left:var(--margin);right:var(--margin);top:0;bottom:0;
52
+ background-image:
53
+ repeating-linear-gradient(to bottom,var(--g-base) 0 1px,transparent 1px var(--lh)),
54
+ repeating-linear-gradient(to bottom,var(--g-base-min) 0 1px,transparent 1px var(--bl));}
55
+ .guides .mline{position:absolute;top:0;bottom:0;width:1px;background:var(--g-edge);}
56
+ .guides .mline.l{left:var(--margin);} .guides .mline.r{right:var(--margin);}
57
+
58
+ .toggle{position:fixed;top:18px;right:18px;z-index:200;display:flex;align-items:center;gap:10px;
59
+ background:var(--ink);color:#fff;border:none;cursor:pointer;font-family:"Space Mono",monospace;
60
+ font-size:12px;letter-spacing:.14em;text-transform:uppercase;padding:11px 14px;}
61
+ .toggle .dot{width:9px;height:9px;border-radius:50%;background:#555;}
62
+ body.grid-on .toggle{background:var(--accent);} body.grid-on .toggle .dot{background:#fff;}
63
+
64
+ /* ===== COCKPIT STYLES ===== */
65
+ header { padding: var(--lh) 0; background: var(--paper); color: var(--ink); display: flex; gap: var(--gutter); flex-wrap: wrap; border-bottom: 2px solid var(--ink); margin-bottom: var(--lh); }
66
+ header strong { color: var(--accent); }
67
+ header span { color: var(--ink-soft); }
68
+
69
+ section { padding: var(--lh) 0; border-top: 1px solid #ddd; }
70
+ section h2 { margin: 0 0 var(--lh) 0; font-size: 16px; line-height: var(--lh); text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-soft); }
71
+ .epic { padding: 0 0 calc(var(--lh) - 1px) 0; margin-bottom: var(--lh); border-bottom: 1px solid #eee; }
72
+ .epic:last-child { margin-bottom: 0; border-bottom: none; }
73
+ .epic strong { color: var(--ink); }
16
74
  .done { color: #0a7; }
17
75
  .pending { color: #c80; }
18
- #err { color: #c00; padding: 1rem; }
76
+ #err { color: #c00; padding: var(--lh) 0; grid-column: 1 / -1; }
19
77
  </style>
20
78
  </head>
21
79
  <body>
22
- <header id="hdr">Loading…</header>
23
- <main>
24
- <section id="planning"><h2>Planning</h2><div id="planning-body"></div></section>
25
- <section id="build"><h2>Epics &amp; status</h2><div id="build-body"></div></section>
26
- </main>
27
- <div id="err" hidden></div>
80
+ <div class="spread">
81
+ <div class="wrap">
82
+ <div class="grid">
83
+ <div class="band">
84
+ <header id="hdr" class="opt-align" style="grid-column: 1 / -1;">Loading…</header>
85
+ </div>
86
+ <div class="band">
87
+ <div id="err" hidden style="grid-column: 1 / -1;"></div>
88
+ <section id="planning" style="grid-column: 1 / 7;"><h2 class="opt-align">Planning</h2><div id="planning-body"></div></section>
89
+ <section id="build" style="grid-column: 7 / 13;"><h2 class="opt-align">Epics &amp; status</h2><div id="build-body"></div></section>
90
+ </div>
91
+ <!-- GRID GUIDES -->
92
+ <div class="guides" aria-hidden="true">
93
+ <div class="cols"></div><div class="rows"></div>
94
+ <div class="mline l"></div><div class="mline r"></div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+
99
+ <button id="gridToggle" class="toggle"><span class="dot"></span> <span class="lbl">Show grid</span> <kbd>G</kbd></button>
100
+
28
101
  <script>
29
102
  const params = new URLSearchParams(location.search);
30
103
  const projectDir = params.get('projectDir') || '';
@@ -61,6 +134,40 @@
61
134
  document.getElementById('err').hidden = false;
62
135
  document.getElementById('err').textContent = e.message;
63
136
  });
137
+
138
+ /* ===== GRID SCRIPT ===== */
139
+ var btn=document.getElementById('gridToggle');
140
+ function setGrid(on){document.body.classList.toggle('grid-on',on);
141
+ if(btn){btn.setAttribute('aria-pressed',on?'true':'false');
142
+ var l=btn.querySelector('.lbl'); if(l) l.textContent=on?'Hide grid':'Show grid';}}
143
+ if(btn) btn.addEventListener('click',function(){setGrid(!document.body.classList.contains('grid-on'));});
144
+ document.addEventListener('keydown',function(e){
145
+ if((e.key==='g'||e.key==='G')&&!e.metaKey&&!e.ctrlKey&&!e.altKey){
146
+ setGrid(!document.body.classList.contains('grid-on'));}});
147
+
148
+ document.querySelectorAll('.guides .cols').forEach(function(h){
149
+ var n=getComputedStyle(document.documentElement).getPropertyValue('--cols').trim()||'12';
150
+ for(var i=1;i<=parseInt(n,10);i++){var c=document.createElement('div');c.className='col';
151
+ var s=document.createElement('span');s.textContent=i;c.appendChild(s);h.appendChild(c);}});
152
+
153
+ (function(){
154
+ var cvs=document.createElement('canvas'),ctx=cvs.getContext('2d');
155
+ var sel='.opt-align';
156
+ function align(){
157
+ document.querySelectorAll(sel).forEach(function(el){
158
+ el.style.marginLeft='0px';
159
+ var cs=getComputedStyle(el),ch=(el.textContent||'').trim().charAt(0); if(!ch) return;
160
+ if(cs.textTransform==='uppercase') ch=ch.toUpperCase();
161
+ ctx.font=cs.fontStyle+' '+cs.fontWeight+' '+cs.fontSize+' '+cs.fontFamily;
162
+ ctx.textAlign='left';
163
+ var abl=ctx.measureText(ch).actualBoundingBoxLeft;
164
+ if(isFinite(abl)) el.style.marginLeft=abl.toFixed(2)+'px';
165
+ });
166
+ }
167
+ if(document.fonts&&document.fonts.ready){document.fonts.ready.then(align);}
168
+ align();
169
+ var t;window.addEventListener('resize',function(){clearTimeout(t);t=setTimeout(align,120);});
170
+ })();
64
171
  </script>
65
172
  </body>
66
173
  </html>
@@ -5,14 +5,17 @@
5
5
  <title>Bigpowers Dashboard</title>
6
6
  <style>
7
7
  /*
8
- * DASHBOARD FRAME TEMPLATE
8
+ * DASHBOARD FRAME TEMPLATE - MULLER-BROCKMANN GRID
9
9
  */
10
+ :root{
11
+ --cols:12;
12
+ --bl:8px;
13
+ --lh:24px;
14
+ --gutter:24px;
15
+ --margin:72px;
16
+ --pad:48px;
17
+ --maxw:1296px;
10
18
 
11
- * { box-sizing: border-box; margin: 0; padding: 0; }
12
- html, body { height: 100%; overflow: hidden; }
13
-
14
- /* ===== THEME VARIABLES ===== */
15
- :root {
16
19
  --bg-primary: #f5f5f7;
17
20
  --bg-secondary: #ffffff;
18
21
  --bg-tertiary: #e5e5e7;
@@ -27,6 +30,11 @@
27
30
  --error: #ff3b30;
28
31
  --selected-bg: #e8f4fd;
29
32
  --selected-border: #0071e3;
33
+
34
+ --g-col:rgba(0,113,227,.075);
35
+ --g-edge:rgba(0,113,227,.40);
36
+ --g-base:rgba(0,150,140,.34);
37
+ --g-base-min:rgba(0,150,140,.12);
30
38
  }
31
39
 
32
40
  @media (prefers-color-scheme: dark) {
@@ -42,44 +50,85 @@
42
50
  --accent-hover: #409cff;
43
51
  --selected-bg: rgba(10, 132, 255, 0.15);
44
52
  --selected-border: #0a84ff;
53
+ --g-col:rgba(10,132,255,.075);
54
+ --g-edge:rgba(10,132,255,.40);
45
55
  }
46
56
  }
47
57
 
58
+ * { box-sizing: border-box; margin: 0; padding: 0; }
59
+ html, body { height: 100%; overflow: hidden; }
60
+
48
61
  body {
49
62
  font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
50
63
  background: var(--bg-primary);
51
64
  color: var(--text-primary);
52
65
  display: flex;
53
66
  flex-direction: column;
54
- line-height: 1.5;
67
+ line-height: var(--lh);
68
+ font-size: 16px;
69
+ }
70
+
71
+ /* ---- scaffold ---- */
72
+ .spread{position:relative;width:100%;}
73
+ .wrap{position:relative;max-width:var(--maxw);margin:0 auto;padding:var(--pad) var(--margin);}
74
+ .grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);
75
+ column-gap:var(--gutter);row-gap:var(--lh);}
76
+ .band{grid-column:1 / -1;display:grid;grid-template-columns:subgrid;
77
+ column-gap:var(--gutter);row-gap:var(--lh);align-items:start;}
78
+ @supports not (grid-template-columns:subgrid){
79
+ .band{grid-template-columns:repeat(var(--cols),1fr);}
55
80
  }
56
81
 
82
+ /* ---- toggle & guides ---- */
83
+ .guides{position:absolute;inset:0;pointer-events:none;z-index:60;opacity:0;
84
+ transition:opacity .26s ease;}
85
+ body.grid-on .guides{opacity:1;}
86
+ .guides .cols{position:absolute;top:0;bottom:0;left:var(--margin);right:var(--margin);
87
+ display:grid;grid-template-columns:repeat(var(--cols),1fr);column-gap:var(--gutter);}
88
+ .guides .col{background:var(--g-col);
89
+ box-shadow:inset 1px 0 0 var(--g-edge),inset -1px 0 0 var(--g-edge);position:relative;}
90
+ .guides .col span{position:absolute;top:32px;left:0;right:0;text-align:center;
91
+ font-family:"Space Mono",monospace;font-size:10px;line-height:1;color:var(--accent);}
92
+ .guides .rows{position:absolute;left:var(--margin);right:var(--margin);top:0;bottom:0;
93
+ background-image:
94
+ repeating-linear-gradient(to bottom,var(--g-base) 0 1px,transparent 1px var(--lh)),
95
+ repeating-linear-gradient(to bottom,var(--g-base-min) 0 1px,transparent 1px var(--bl));}
96
+ .guides .mline{position:absolute;top:0;bottom:0;width:1px;background:var(--g-edge);}
97
+ .guides .mline.l{left:var(--margin);} .guides .mline.r{right:var(--margin);}
98
+
99
+ .toggle{position:fixed;top:18px;right:18px;z-index:200;display:flex;align-items:center;gap:10px;
100
+ background:var(--bg-secondary);color:var(--text-primary);border:1px solid var(--border);cursor:pointer;font-family:"Space Mono",monospace;
101
+ font-size:12px;letter-spacing:.14em;text-transform:uppercase;padding:11px 14px;border-radius:4px;}
102
+ .toggle .dot{width:9px;height:9px;border-radius:50%;background:var(--text-tertiary);}
103
+ body.grid-on .toggle{background:var(--accent);color:#fff;border-color:var(--accent);} body.grid-on .toggle .dot{background:#fff;}
104
+
57
105
  /* ===== FRAME STRUCTURE ===== */
58
106
  .header {
59
107
  background: var(--bg-secondary);
60
- padding: 0.5rem 1.5rem;
108
+ padding: var(--lh) var(--margin);
61
109
  display: flex;
62
110
  justify-content: space-between;
63
111
  align-items: center;
64
112
  border-bottom: 1px solid var(--border);
65
113
  flex-shrink: 0;
66
114
  }
67
- .header h1 { font-size: 0.85rem; font-weight: 500; color: var(--text-secondary); }
68
- .header .status { font-size: 0.7rem; color: var(--success); display: flex; align-items: center; gap: 0.4rem; }
115
+ .header h1 { font-size: 16px; font-weight: 500; color: var(--text-secondary); line-height: var(--lh); margin: 0; }
116
+ .header .status { font-size: 12px; color: var(--success); display: flex; align-items: center; gap: 8px; line-height: var(--lh); }
69
117
  .header .status::before { content: ''; width: 6px; height: 6px; background: var(--success); border-radius: 50%; }
70
118
 
71
- .main { flex: 1; overflow-y: auto; }
72
- #claude-content { padding: 2rem; min-height: 100%; }
119
+ .main { flex: 1; overflow-y: auto; position: relative; }
120
+ #claude-content { min-height: 100%; }
73
121
 
74
122
  .indicator-bar {
75
123
  background: var(--bg-secondary);
76
124
  border-top: 1px solid var(--border);
77
- padding: 0.5rem 1.5rem;
125
+ padding: var(--bl) var(--margin);
78
126
  flex-shrink: 0;
79
127
  text-align: center;
128
+ line-height: var(--lh);
80
129
  }
81
130
  .indicator-bar span {
82
- font-size: 0.75rem;
131
+ font-size: 12px;
83
132
  color: var(--text-secondary);
84
133
  }
85
134
  .indicator-bar .selected-text {
@@ -88,42 +137,43 @@
88
137
  }
89
138
 
90
139
  /* ===== TYPOGRAPHY ===== */
91
- h2 { font-size: 1.5rem; font-weight: 600; margin-bottom: 0.5rem; }
92
- h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.25rem; }
93
- .subtitle { color: var(--text-secondary); margin-bottom: 1.5rem; }
94
- .section { margin-bottom: 2rem; }
95
- .label { font-size: 0.7rem; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
140
+ h2 { font-size: 24px; line-height: calc(var(--lh) * 1.5); font-weight: 600; margin-bottom: var(--lh); grid-column: 1 / -1; }
141
+ h3 { font-size: 16px; line-height: var(--lh); font-weight: 600; margin-bottom: var(--bl); }
142
+ .subtitle { color: var(--text-secondary); margin-bottom: var(--lh); grid-column: 1 / -1; }
143
+ .section { margin-bottom: calc(var(--lh) * 2); grid-column: 1 / -1; }
144
+ .label { font-size: 12px; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--bl); line-height: var(--lh); }
96
145
 
97
146
  /* ===== OPTIONS ===== */
98
- .options { display: flex; flex-direction: column; gap: 0.75rem; }
147
+ .options { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; row-gap: var(--lh); }
99
148
  .option {
100
149
  background: var(--bg-secondary);
101
150
  border: 2px solid var(--border);
102
151
  border-radius: 12px;
103
- padding: 1rem 1.25rem;
152
+ padding: calc(var(--lh) - 2px) 1.25rem;
104
153
  cursor: pointer;
105
154
  transition: all 0.15s ease;
106
155
  display: flex;
107
156
  align-items: flex-start;
108
- gap: 1rem;
157
+ gap: var(--gutter);
158
+ grid-column: span 6; /* Options span 6 cols each */
109
159
  }
110
160
  .option:hover { border-color: var(--accent); }
111
161
  .option.selected { background: var(--selected-bg); border-color: var(--selected-border); }
112
162
  .option .letter {
113
163
  background: var(--bg-tertiary);
114
164
  color: var(--text-secondary);
115
- width: 1.75rem; height: 1.75rem;
165
+ width: calc(var(--lh) * 1.5); height: calc(var(--lh) * 1.5);
116
166
  border-radius: 6px;
117
167
  display: flex; align-items: center; justify-content: center;
118
- font-weight: 600; font-size: 0.85rem; flex-shrink: 0;
168
+ font-weight: 600; font-size: 16px; flex-shrink: 0;
119
169
  }
120
170
  .option.selected .letter { background: var(--accent); color: white; }
121
171
  .option .content { flex: 1; }
122
- .option .content h3 { font-size: 0.95rem; margin-bottom: 0.15rem; }
123
- .option .content p { color: var(--text-secondary); font-size: 0.85rem; margin: 0; }
172
+ .option .content h3 { font-size: 16px; margin-bottom: var(--bl); }
173
+ .option .content p { color: var(--text-secondary); font-size: 14px; margin: 0; line-height: var(--lh); }
124
174
 
125
175
  /* ===== CARDS ===== */
126
- .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; }
176
+ .cards { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; row-gap: var(--lh); }
127
177
  .card {
128
178
  background: var(--bg-secondary);
129
179
  border: 1px solid var(--border);
@@ -131,13 +181,14 @@
131
181
  overflow: hidden;
132
182
  cursor: pointer;
133
183
  transition: all 0.15s ease;
184
+ grid-column: span 4; /* Cards span 4 cols each (3 per row) */
134
185
  }
135
186
  .card:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
136
187
  .card.selected { border-color: var(--selected-border); border-width: 2px; }
137
- .card-image { background: var(--bg-tertiary); aspect-ratio: 16/10; display: flex; align-items: center; justify-content: center; }
138
- .card-body { padding: 1rem; }
139
- .card-body h3 { margin-bottom: 0.25rem; }
140
- .card-body p { color: var(--text-secondary); font-size: 0.85rem; }
188
+ .card-image { background: var(--bg-tertiary); height: calc(var(--lh) * 6); display: flex; align-items: center; justify-content: center; }
189
+ .card-body { padding: calc(var(--lh) - 1px) var(--gutter); }
190
+ .card-body h3 { margin-bottom: var(--bl); }
191
+ .card-body p { color: var(--text-secondary); font-size: 14px; line-height: var(--lh); margin: 0; }
141
192
 
142
193
  /* ===== MOCKUP CONTAINER ===== */
143
194
  .mockup {
@@ -145,28 +196,30 @@
145
196
  border: 1px solid var(--border);
146
197
  border-radius: 12px;
147
198
  overflow: hidden;
148
- margin-bottom: 1.5rem;
199
+ margin-bottom: var(--lh);
200
+ grid-column: 1 / -1;
149
201
  }
150
202
  .mockup-header {
151
203
  background: var(--bg-tertiary);
152
- padding: 0.5rem 1rem;
153
- font-size: 0.75rem;
204
+ padding: calc(var(--bl) - 1px) var(--gutter);
205
+ font-size: 12px;
206
+ line-height: var(--lh);
154
207
  color: var(--text-secondary);
155
208
  border-bottom: 1px solid var(--border);
156
209
  }
157
- .mockup-body { padding: 1.5rem; }
210
+ .mockup-body { padding: var(--lh) var(--gutter); }
158
211
 
159
212
  /* ===== SPLIT VIEW ===== */
160
- .split { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
161
- @media (max-width: 700px) { .split { grid-template-columns: 1fr; } }
213
+ .split { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; }
214
+ .split > div { grid-column: span 6; }
162
215
 
163
216
  /* ===== PROS/CONS ===== */
164
- .pros-cons { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin: 1rem 0; }
165
- .pros, .cons { background: var(--bg-secondary); border-radius: 8px; padding: 1rem; }
166
- .pros h4 { color: var(--success); font-size: 0.85rem; margin-bottom: 0.5rem; }
167
- .cons h4 { color: var(--error); font-size: 0.85rem; margin-bottom: 0.5rem; }
168
- .pros ul, .cons ul { margin-left: 1.25rem; font-size: 0.85rem; color: var(--text-secondary); }
169
- .pros li, .cons li { margin-bottom: 0.25rem; }
217
+ .pros-cons { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; margin-bottom: var(--lh); }
218
+ .pros, .cons { background: var(--bg-secondary); border-radius: 8px; padding: calc(var(--lh) - 1px) var(--gutter); grid-column: span 6; border: 1px solid var(--border); }
219
+ .pros h4 { color: var(--success); font-size: 14px; margin-bottom: var(--bl); line-height: var(--lh); }
220
+ .cons h4 { color: var(--error); font-size: 14px; margin-bottom: var(--bl); line-height: var(--lh); }
221
+ .pros ul, .cons ul { margin-left: 1.25rem; font-size: 14px; color: var(--text-secondary); }
222
+ .pros li, .cons li { margin-bottom: var(--bl); }
170
223
  </style>
171
224
  </head>
172
225
  <body>
@@ -176,8 +229,54 @@
176
229
  </div>
177
230
 
178
231
  <div class="main">
179
- <div id="claude-content">
180
- <!-- CONTENT -->
232
+ <div id="claude-content" class="spread">
233
+ <div class="wrap">
234
+ <div class="grid">
235
+ <div class="band section">
236
+ <h2 class="opt-align">Dashboard Template</h2>
237
+ <p class="subtitle">Mockup and structure using the new 12-column Müller-Brockmann grid.</p>
238
+ </div>
239
+
240
+ <div class="band section split">
241
+ <div>
242
+ <h3 class="opt-align">Split Left (6 cols)</h3>
243
+ <p>Aligned precisely to the grid. All baselines lock to 8px.</p>
244
+ </div>
245
+ <div>
246
+ <h3 class="opt-align">Split Right (6 cols)</h3>
247
+ <p>The gap between is precisely one gutter (24px).</p>
248
+ </div>
249
+ </div>
250
+
251
+ <div class="band section cards">
252
+ <div class="card">
253
+ <div class="card-image">Img 1</div>
254
+ <div class="card-body">
255
+ <h3>Card 1 (4 cols)</h3>
256
+ <p>Three cards fit perfectly in 12 columns.</p>
257
+ </div>
258
+ </div>
259
+ <div class="card">
260
+ <div class="card-image">Img 2</div>
261
+ <div class="card-body">
262
+ <h3>Card 2 (4 cols)</h3>
263
+ <p>Aligned on the baseline.</p>
264
+ </div>
265
+ </div>
266
+ <div class="card selected">
267
+ <div class="card-image">Img 3</div>
268
+ <div class="card-body">
269
+ <h3>Card 3 (4 cols)</h3>
270
+ <p>This card is selected.</p>
271
+ </div>
272
+ </div>
273
+ </div>
274
+ <!-- GRID GUIDES -->
275
+ <div class="guides" aria-hidden="true">
276
+ <div class="cols"></div><div class="rows"></div>
277
+ <div class="mline l"></div><div class="mline r"></div>
278
+ </div>
279
+ </div>
181
280
  </div>
182
281
  </div>
183
282
 
@@ -185,5 +284,42 @@
185
284
  <span id="indicator-text">Interact with the dashboard, then return to the terminal</span>
186
285
  </div>
187
286
 
287
+ <button id="gridToggle" class="toggle"><span class="dot"></span> <span class="lbl">Show grid</span> <kbd>G</kbd></button>
288
+
289
+ <script>
290
+ /* ===== GRID SCRIPT ===== */
291
+ var btn=document.getElementById('gridToggle');
292
+ function setGrid(on){document.body.classList.toggle('grid-on',on);
293
+ if(btn){btn.setAttribute('aria-pressed',on?'true':'false');
294
+ var l=btn.querySelector('.lbl'); if(l) l.textContent=on?'Hide grid':'Show grid';}}
295
+ if(btn) btn.addEventListener('click',function(){setGrid(!document.body.classList.contains('grid-on'));});
296
+ document.addEventListener('keydown',function(e){
297
+ if((e.key==='g'||e.key==='G')&&!e.metaKey&&!e.ctrlKey&&!e.altKey){
298
+ setGrid(!document.body.classList.contains('grid-on'));}});
299
+
300
+ document.querySelectorAll('.guides .cols').forEach(function(h){
301
+ var n=getComputedStyle(document.documentElement).getPropertyValue('--cols').trim()||'12';
302
+ for(var i=1;i<=parseInt(n,10);i++){var c=document.createElement('div');c.className='col';
303
+ var s=document.createElement('span');s.textContent=i;c.appendChild(s);h.appendChild(c);}});
304
+
305
+ (function(){
306
+ var cvs=document.createElement('canvas'),ctx=cvs.getContext('2d');
307
+ var sel='.opt-align';
308
+ function align(){
309
+ document.querySelectorAll(sel).forEach(function(el){
310
+ el.style.marginLeft='0px';
311
+ var cs=getComputedStyle(el),ch=(el.textContent||'').trim().charAt(0); if(!ch) return;
312
+ if(cs.textTransform==='uppercase') ch=ch.toUpperCase();
313
+ ctx.font=cs.fontStyle+' '+cs.fontWeight+' '+cs.fontSize+' '+cs.fontFamily;
314
+ ctx.textAlign='left';
315
+ var abl=ctx.measureText(ch).actualBoundingBoxLeft;
316
+ if(isFinite(abl)) el.style.marginLeft=abl.toFixed(2)+'px';
317
+ });
318
+ }
319
+ if(document.fonts&&document.fonts.ready){document.fonts.ready.then(align);}
320
+ align();
321
+ var t;window.addEventListener('resize',function(){clearTimeout(t);t=setTimeout(align,120);});
322
+ })();
323
+ </script>
188
324
  </body>
189
325
  </html>