fix(ms-ai-architect): playground v1.13.0 — visuelle DS-bugs

Fix-pakke som speiler llm-security v7.6.1 (commit f9b555a). Samme klasse
visuelle bugs identifisert via parallell DS-analyse av playground-rendrere.

- B1: renderFindingsBlock + renderRequirements bytter <div class="findings">
  outer (DS grid 360px+1fr klemte indre struktur til 360px-kolonne, lot
  1fr-detail-panel-kolonnen stå tom) til <section class="report-meta">.
  BEM-strukturen findings__list > findings__group > findings__items uendret.
- B2: lokal .report-table CSS for 6+ rapporter (Trusler, Kostnadsoversikt,
  TCO, Risiko-tabell, Key Metrics) som manglet styling — DS implementerer
  ikke klassen. Speilet lokal styling fra llm-security v7.6.1.
- B3: ROS-matrise-bobler bytter <span> til <button type="button"
  data-threat-id="..." aria-label="..."> med document-level click-handler
  som scroller smooth til tilsvarende rad i Trusler-tabellen og
  highlighter raden i 1.6 sek. Lokal CSS for cursor:pointer, hover
  scale(1.15), :focus-visible outline.
- B4: renderRadarSvg bumpet 300x300 til 380x380, R fra 100 til 125,
  label-offset fra R+25 til R+28, dynamisk text-anchor basert på
  horisontal-posisjon for å unngå at bottom-labels overlapper hverandre
  ved 6+ akser (typisk for ROS-rapport med 7 risiko-dimensjoner).
- B5: lokal .recommendation-card__body { overflow-wrap: anywhere;
  word-break: break-word } for å forhindre at lange single-line tekster
  (URLer, owner-tags, dato) skubber innhold ut av viewport i grid-cellen.

tests/test-playground-v3.sh: DS-klasse-assertion oppdatert fra .findings
til .findings__list (BEM-list er fortsatt i bruk; outer grid-container
bevisst fjernet i B1).

Verifisering:
- 22/22 smoke-test PASS (B1-B5 grep-asserts)
- 271/271 playground E2E PASS (201 statisk-struktur + 70 parser-fixtures)
- 219 plugin-validering PASS
- 42 KB-update test PASS

Versjon: v1.12.0 -> v1.13.0 (plugin.json, README badge, README
version-history, CHANGELOG, ROADMAP, TODO, plugin CLAUDE.md
playground-header, root README plugin-list, root CLAUDE.md plugin-list).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-06 14:51:15 +02:00
commit 121c5cc677
8 changed files with 108 additions and 17 deletions

View file

@ -11,7 +11,7 @@ plugins/
graceful-handoff/ v2.1.0 — Auto-trigger handoff via Stop hook (skill + JSON pipeline + 4-step model-aware context resolution)
linkedin-thought-leadership/ v1.2.0 — LinkedIn content pipeline + analytics
llm-security/ v6.0.0 — Security scanning, auditing, threat modeling
ms-ai-architect/ v1.12.0 — Microsoft AI architecture (Cosmo Skyberg persona) + manual KB-refresh slash command
ms-ai-architect/ v1.13.0 — Microsoft AI architecture (Cosmo Skyberg persona) + manual KB-refresh slash command
okr/ v1.0.0 — OKR guidance for Norwegian public sector
voyage/ v4.0.0 — Brief, research, plan, execute, review, continue. Contract-driven Claude Code pipeline (six-command universal pipeline + multi-session resumption + --gates autonomy chain)

View file

@ -164,7 +164,7 @@ Key command: `/graceful-handoff [topic-slug] [--no-commit] [--no-push] [--dry-ru
---
### [MS AI Architect — Azure AI and Microsoft Foundry](plugins/ms-ai-architect/) `v1.12.0` `🇳🇴 Norwegian`
### [MS AI Architect — Azure AI and Microsoft Foundry](plugins/ms-ai-architect/) `v1.13.0` `🇳🇴 Norwegian`
Microsoft AI solution architecture guidance for Norwegian public sector and enterprise.

View file

@ -1,6 +1,6 @@
{
"name": "ms-ai-architect",
"version": "1.12.0",
"version": "1.13.0",
"description": "Microsoft AI Solution Architect - structured architecture guidance for the full Microsoft AI stack",
"author": {
"name": "Kjell Tore Guttormsen"

View file

@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.13.0] - 2026-05-06
### Fixed — playground visuelle DS-bugs
Fix-pakke som speiler llm-security v7.6.1 (commit `f9b555a`). Samme klasse av visuelle bugs identifisert i ms-ai-architect playground via parallell DS-analyse. Verifisert i 22 smoke-test-asserts + full E2E-regresjon.
- **B1: `renderFindingsBlock` + `renderRequirements` outer-wrapper.** DS' `.findings` er `display: grid; grid-template-columns: 360px 1fr` (list+detail-panel). Playground bruker bare list-kolonnen, så outer `<div class="findings">` klemte indre struktur til 360px og forlot 1fr-kolonnen tom. Erstattet med `<section class="report-meta">`-wrapper. Indre `findings__list > findings__group > findings__group-header + findings__items` BEM-struktur uendret.
- **B2: `.report-table` lokal CSS.** Brukt i 6+ rapporter (Trusler, Kostnadsoversikt, TCO, Risiko-tabell, Key Metrics) men ikke definert i vendored DS — rendret som ren ustylet tekst-grid. Lokal styling speilet fra llm-security v7.6.1: 100%-bredde, border-collapse, `bg-soft` th-bakgrunn med uppercase-overskrift, hover-rad, mono-formatert `<code>`-inline.
- **B3: ROS-matrise klikkbare bobler.** `<span class="matrix__bubble">` byttet til `<button type="button" data-threat-id="…" aria-label="Trussel: …">`. Document-level click-handler matcher `.matrix__bubble[data-threat-id]`, scroller smooth til tilsvarende rad i Trusler-tabellen og highlighter raden i 1.6 sek. Lokal CSS for `cursor:pointer`, `transform: scale(1.15)` på hover, `:focus-visible` outline.
- **B4: `renderRadarSvg` label-overlap ved 6+ akser.** Bumpe SVG fra 300×300 til 380×380, R fra 100 til 125, label-offset fra `R+25` til `R+28`. Dynamisk `text-anchor` basert på horisontal-posisjon (`Math.abs(dx) < 0.2 ? 'middle' : (dx > 0 ? 'start' : 'end')`) for å unngå at bottom-labels overlapper hverandre — typisk for ROS-rapporten med 7 risiko-dimensjoner.
- **B5: `recommendation-card__body` overflow-wrap.** Lange single-line tekster (URLer, owner-tags, datoer) skubbet ut av viewport i grid-cellen `auto + 1fr`. Lokal CSS `overflow-wrap: anywhere; word-break: break-word;`.
### Changed
- `tests/test-playground-v3.sh`: DS-klasse-assertion oppdatert fra `.findings` til `.findings__list` (BEM-list er fortsatt i bruk; outer grid-container bevisst fjernet i B1).
### Notes on 1.13.0
- Bugfix-only release. Ingen scanner-/agent-/knowledge-endringer. Ingen modifisering av `playground/vendor/` (DS-endringer der må gå via `shared/playground-design-system/` + re-sync).
- Playground v3-arkitektur uendret. Alle 17 rapport-renderers og parser-routing-tabell uendret.
- Smoke-test: 22/22 PASS (`/private/tmp/claude-smoke-msarch-v1130.mjs`). E2E playground: 272/272 PASS (201 statisk-struktur etter test-oppdatering + 70 parser-fixtures + 1 verdict-pill). Plugin-validering: 219 PASS. KB-update: 42 PASS.
## [1.12.0] - 2026-05-05
### Added — Manuell KB-refresh-arbeidsflyt

View file

@ -187,9 +187,9 @@ claude --plugin ./plugins/ms-ai-architect
/architect:help
```
## Playground (v3 / v1.11.0)
## Playground (v3 / v1.13.0)
Interaktiv decision-builder + rapport-viewer for Microsoft AI-beslutninger. Erstatter v2 5-stegs-pipelinen med en multi-surface-app som persisterer state og visualiserer importerte rapporter inline. Spec: v3-arkitektur dokumentert under `.claude/projects/2026-05-03-playground-v3-architecture/`. v1.10.0-utvidelser dokumentert under `.claude/projects/2026-05-03-ms-ai-architect-v1-10-playground/`. v1.11.0 leverer design-system 100%-adoption (PARALLEL-CSS-migrasjon til DS-konvensjon, inline `<style>`-trim 37%, severity-coded card borders, app-header-restruktur, `.stack-lg` body spacing, AI Act-pyramide bredde-fix).
Interaktiv decision-builder + rapport-viewer for Microsoft AI-beslutninger. Erstatter v2 5-stegs-pipelinen med en multi-surface-app som persisterer state og visualiserer importerte rapporter inline. Spec: v3-arkitektur dokumentert under `.claude/projects/2026-05-03-playground-v3-architecture/`. v1.10.0-utvidelser dokumentert under `.claude/projects/2026-05-03-ms-ai-architect-v1-10-playground/`. v1.11.0 leverer design-system 100%-adoption (PARALLEL-CSS-migrasjon til DS-konvensjon, inline `<style>`-trim 37%, severity-coded card borders, app-header-restruktur, `.stack-lg` body spacing, AI Act-pyramide bredde-fix). v1.13.0 leverer fix-pakke som speiler llm-security v7.6.1 — 5 visuelle bugs fikset: `renderFindingsBlock`/`renderRequirements` outer-wrapper bytter `<div class="findings">` (DS grid 360px+1fr klemte indre struktur) til `<section class="report-meta">`; lokal `.report-table` CSS for 6+ rapporter; ROS-matrise-bobler bytter `<span>``<button>` med `data-threat-id` + click-handler som scroller til Trusler-tabell-rad; `renderRadarSvg` bumpet 300×300→380×380 med dynamisk `text-anchor` for å unngå label-overlap ved 6+ akser; `recommendation-card__body` overflow-wrap.
- **Fil:** `playground/ms-ai-architect-playground.html` (~3870+ linjer, single-file v3-arkitektur)
- **4 surfaces:** Onboarding (18 felles felt — 4 strukturerte / 14 fritekst etter v1.10.0) → Home (prosjekt-liste + 3 entry-tracks) → Catalog (25 commands gruppert i 5 expansion-grupper med søk) → Project (per-prosjekt tabs, command-form-prefill fra felles state, paste-back-import med rapport-visualisering)

View file

@ -6,7 +6,7 @@
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
![Version](https://img.shields.io/badge/version-1.12.0-blue)
![Version](https://img.shields.io/badge/version-1.13.0-blue)
![Platform](https://img.shields.io/badge/platform-Claude_Code_Plugin-purple)
![Docs](https://img.shields.io/badge/reference_docs-387-green)
![Agents](https://img.shields.io/badge/agents-12-orange)
@ -638,6 +638,7 @@ Category-to-skill routing is defined in `scripts/skill-gen/category-skill-map.js
| Version | Date | Highlights |
|---------|------|-----------|
| **1.13.0** | 2026-05-06 | Playground visual DS-fixes — 5 bugs identifisert og fikset i fix-pakke som speiler llm-security v7.6.1: (B1) `renderFindingsBlock` + `renderRequirements` outer-wrapper byttet fra `<div class="findings">` (DS grid 360px+1fr klemte indre struktur) til `<section class="report-meta">`; (B2) lokal `.report-table` CSS for 6+ rapporter (Trusler, Kostnadsoversikt, TCO, Risiko, Key Metrics) som manglet styling; (B3) ROS-matrise-bobler byttet `<span>``<button>` med `data-threat-id` + click-handler som scroller til Trusler-tabell-rad og highlighter; (B4) `renderRadarSvg` bumpet 300×300→380×380, R=125, dynamisk `text-anchor` for å unngå label-overlap ved 6+ akser; (B5) `recommendation-card__body` overflow-wrap. 22/22 smoke-test PASS. 219 plugin-validering. 272 E2E. |
| **1.12.0** | 2026-05-05 | Manuell KB-refresh-arbeidsflyt — ny `/architect:kb-update` slash-kommando som driver poll → endringsrapport → `microsoft_docs_fetch`-oppdatering → commit fra en aktiv Claude Code-sesjon. Schedulering er bevisst utenfor scope og overlatt til brukeren. Tidligere launchd/cron-arkitektur (Wave 3-5: install-kb-cron, weekly-kb-cron, plist/systemd/Windows-templates, auth-mode-validation, lock-file, cost-cap, kb-update-status surfacing i session-start-hook) fjernet — ~1500 linjer kode + 7 testmoduler ut. Holder pluginen klart innenfor Anthropic Consumer Terms § 3 (automated access only via API key or where explicitly permitted). Beholdte utilities (atomic-write, backup, cross-platform-paths, log-rotate) + run-weekly-update + report-changes + build-registry + discover-new-urls fortsatt fullt funksjonelle for change-detection-fasen. 42/42 KB-update-tester PASS. |
| **1.11.0** | 2026-05-04 | Design-system 100%-adoption — 13 generic components hoisted to shared playground-design-system v0.3.0, all PARALLEL CSS names migrated to DS conventions, inline `<style>` block trimmed 37% (202 → 127 lines), severity-coded card borders on report cards, app-header restructure with breadcrumb, `.stack-lg` body spacing across home/project/catalog, AI Act pyramide width fix. Demo state renamed to "Acme Kommune" + "Acme: Kunde-chatbot" for cross-fixture consistency. 24 v1.11.0 screenshots regenerated. 278/278 playground E2E PASS. |
| **1.6.0** | 2026-02-19 | ROS analysis command and agent (`/architect:ros`) — 7-dimension risk assessment with NS 5814/ISO 31000 methodology, 49-threat AI threat library, sector-specific checklists (health, transport, finance, justice, education), MAESTRO multi-agent security model, 7 new KB reference documents (3,131 lines), E2E test suite (24 checks), summary-agent integration |

View file

@ -168,6 +168,29 @@
.suppressed-panel__list { display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-2) 0 0 0; }
.suppressed-panel__item { padding: var(--space-2) var(--space-3); background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-sm); font-size: var(--font-size-sm); color: var(--color-text-secondary); display: flex; gap: var(--space-3); align-items: baseline; }
.suppressed-panel__id { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
/* v1.13.0 fix (B2): .report-table — DS har ikke implementert denne klassen, men
playground-rendrere bruker den i 6+ rapporter (Trusler, Kostnadsoversikt,
TCO, Risiko-tabell, Key Metrics). Lokal styling som komplementerer DS-tokens.
Speilet fra llm-security v7.6.1 commit f9b555a. */
.report-table { width: 100%; border-collapse: collapse; margin: var(--space-3) 0; font-size: var(--font-size-sm); }
.report-table th { text-align: left; padding: 8px 12px; border-bottom: 2px solid var(--color-border-moderate); background: var(--color-bg-soft); font-weight: var(--font-weight-semibold); color: var(--color-text-secondary); text-transform: uppercase; font-size: 11px; letter-spacing: 0.04em; }
.report-table td { padding: 8px 12px; border-bottom: 1px solid var(--color-border-subtle); vertical-align: top; color: var(--color-text-primary); }
.report-table tr:last-child td { border-bottom: none; }
.report-table tbody tr:hover { background: var(--color-bg-soft); }
.report-table code { font-family: var(--font-family-mono); font-size: 12px; background: var(--color-surface-sunken); padding: 1px 6px; border-radius: var(--radius-sm); }
/* v1.13.0 fix (B5): recommendation-card body kan inneholde lange single-line
tekster (URLer, owner-tags, dato). Tving word-wrap så grid-celle (auto + 1fr)
ikke skubber innhold utenfor viewport. */
.recommendation-card__body { overflow-wrap: anywhere; word-break: break-word; }
/* v1.13.0 fix (B3): matrix-bobler i ROS-matrise skal være klikkbare.
DS har hover på cellene, men bobler er <span> uten cursor. Klikk-handler
scroller til tilsvarende rad i Trusler-tabellen. */
.matrix__bubble { cursor: pointer; transition: transform var(--duration-fast) var(--ease-default); }
.matrix__bubble:hover { transform: scale(1.15); }
.matrix__bubble:focus-visible { outline: 2px solid var(--color-primary-500); outline-offset: 2px; }
</style>
</head>
<body>
@ -3272,6 +3295,9 @@
}
function renderFindingsBlock(findings, label) {
// v1.13.0 fix (B1): DS' .findings er grid 360px+1fr (list+detail-panel).
// Vi har bare list-kolonnen; bruk <section class="report-meta"> som outer
// for å beholde findings__list-strukturen uten å bli klemt til 360px.
const items = findings.map(function (f) {
return '<li class="findings__item">' +
'<span class="findings__item-severity-dot" data-severity="' + escapeAttr(f.severity || 'info') + '"></span>' +
@ -3280,14 +3306,15 @@
'<span class="findings__item-meta">Lokasjon: ' + escapeHtml(f.location || '—') + ' · Severity: ' + escapeHtml(f.severity || '—') + '</span>' +
'</li>';
}).join('');
return '<div class="findings">' +
return '<section class="report-meta">' +
'<h4>' + escapeHtml(label) + '</h4>' +
'<div class="findings__list">' +
'<div class="findings__group">' +
'<div class="findings__group-header"><span>' + escapeHtml(label) + '</span><span>' + findings.length + '</span></div>' +
'<ul class="findings__items">' + items + '</ul>' +
'</div>' +
'</div>' +
'</div>';
'</section>';
}
function renderMatrixHtml(data, cons_max) {
@ -3307,12 +3334,14 @@
for (let prob = 1; prob <= probSize; prob++) {
const score = prob * cons;
const items = byPC[prob + '_' + cons] || [];
// v1.13.0 fix (B3): bobler er nå <button> så de er klikkbare og fokuserbare.
// data-threat-id mapper til rad i Trusler-tabellen via document-level handler.
const bubblesHtml = items.length
? '<div class="matrix__cell-bubbles">' +
items.slice(0, 3).map(function (it, i) {
return '<span class="matrix__bubble" title="' + escapeAttr(it.label || '') + '">' + (i + 1) + '</span>';
return '<button type="button" class="matrix__bubble" data-threat-id="' + escapeAttr(it.id || it.label || '') + '" title="' + escapeAttr(it.label || '') + '" aria-label="Trussel: ' + escapeAttr(it.label || it.id || '') + '">' + (i + 1) + '</button>';
}).join('') +
(items.length > 3 ? '<span class="matrix__bubble matrix__bubble--count">+' + (items.length - 3) + '</span>' : '') +
(items.length > 3 ? '<button type="button" class="matrix__bubble matrix__bubble--count" aria-label="' + (items.length - 3) + ' flere trusler">+' + (items.length - 3) + '</button>' : '') +
'</div>'
: '';
html += '<div class="matrix__cell" data-score="' + score + '">' +
@ -3332,8 +3361,13 @@
function renderRadarSvg(axes) {
if (!axes || !axes.length) return '';
// v1.13.0 fix (B4): bump SVG fra 300×300 til 380×380, R fra 100 til 125,
// label-offset fra R+25 til R+28, og dynamisk text-anchor basert på
// horisontal-posisjon. Forhindrer at bottom-labels overlapper ved 6+
// akser (typisk for ROS med 7 risiko-dimensjoner). Speilet fra
// llm-security v7.6.1 commit f9b555a.
const N = axes.length;
const cx = 150, cy = 150, R = 100;
const SIZE = 380, cx = SIZE / 2, cy = SIZE / 2, R = 125;
const points = axes.map(function (a, i) {
const angle = (i / N) * 2 * Math.PI - Math.PI / 2;
const r = R * (Math.max(0, Math.min(5, a.score)) / 5);
@ -3341,9 +3375,11 @@
}).join(' ');
const labels = axes.map(function (a, i) {
const angle = (i / N) * 2 * Math.PI - Math.PI / 2;
const x = cx + (R + 25) * Math.cos(angle);
const y = cy + (R + 25) * Math.sin(angle);
return '<text class="radar__label" x="' + x.toFixed(1) + '" y="' + y.toFixed(1) + '" text-anchor="middle" dominant-baseline="middle">' + escapeHtml(a.name) + '</text>';
const x = cx + (R + 28) * Math.cos(angle);
const y = cy + (R + 28) * Math.sin(angle);
const dx = Math.cos(angle);
const anchor = Math.abs(dx) < 0.2 ? 'middle' : (dx > 0 ? 'start' : 'end');
return '<text class="radar__label" x="' + x.toFixed(1) + '" y="' + y.toFixed(1) + '" text-anchor="' + anchor + '" dominant-baseline="middle">' + escapeHtml(a.name) + '</text>';
}).join('');
const spokes = axes.map(function (a, i) {
const angle = (i / N) * 2 * Math.PI - Math.PI / 2;
@ -3352,7 +3388,7 @@
return '<line class="radar__axis" x1="' + cx + '" y1="' + cy + '" x2="' + x.toFixed(1) + '" y2="' + y.toFixed(1) + '"/>';
}).join('');
return '<div class="radar"><div class="radar__chart">' +
'<svg class="radar__svg" viewBox="0 0 300 300">' +
'<svg class="radar__svg" viewBox="0 0 ' + SIZE + ' ' + SIZE + '">' +
'<circle class="radar__grid-line" cx="' + cx + '" cy="' + cy + '" r="' + R + '" fill="none"/>' +
'<circle class="radar__grid-line" cx="' + cx + '" cy="' + cy + '" r="' + (R * 0.6) + '" fill="none"/>' +
spokes + labels +
@ -3467,7 +3503,9 @@
'</div>';
}).join('') : '';
const body = cardsHtml + (expansionsHtml ? '<div class="findings">' + expansionsHtml + '</div>' : '');
// v1.13.0 fix (B1): bytt .findings-outer til .report-meta-wrapper
// (DS' .findings er grid 360px+1fr, klemmer expansion-list til 360px).
const body = cardsHtml + (expansionsHtml ? '<section class="report-meta">' + expansionsHtml + '</section>' : '');
slot.innerHTML = renderPageShell({
eyebrow: 'KRAV',
title: data.title || 'AI Act-krav per risiko og rolle',
@ -5243,6 +5281,32 @@
if (handler) handler(ev, actionEl);
});
// v1.13.0 fix (B3): matrix-bobler klikkbare. Klikk scroller til tilsvarende
// rad i Trusler-tabellen og fremhever den kort. Bruker data-threat-id som
// anker (matchet mot første kolonne i tabellen). Speilet fra llm-security
// v7.6.1 commit f9b555a.
document.addEventListener('click', function (ev) {
const bubble = ev.target.closest('.matrix__bubble[data-threat-id]');
if (!bubble) return;
const threatId = bubble.getAttribute('data-threat-id');
if (!threatId) return;
const tables = document.querySelectorAll('table.report-table');
for (let t = 0; t < tables.length; t++) {
const rows = tables[t].querySelectorAll('tbody tr');
for (let r = 0; r < rows.length; r++) {
const firstCell = rows[r].querySelector('td');
if (firstCell && firstCell.textContent.trim() === threatId) {
rows[r].scrollIntoView({ behavior: 'smooth', block: 'center' });
const orig = rows[r].style.background;
rows[r].style.background = 'var(--color-primary-100, var(--color-bg-soft))';
rows[r].style.transition = 'background var(--duration-base) var(--ease-default)';
setTimeout(function () { rows[r].style.background = orig; }, 1600);
return;
}
}
}
});
ACTIONS['onboarding-toggle-group'] = function (ev, el) {
const exp = el.closest('.expansion');
if (!exp) return;

View file

@ -198,7 +198,11 @@ fi
# -------------------------------------------------------
# 12. Design-system CSS-klasse-bruk (Tier 1+2+3)
# -------------------------------------------------------
DS_CLASSES=".pyramide .matrix .radar .findings .distribution .critique-card .capability-matrix .aiact-timeline .tracks .error-summary .guide-panel .expansion .form-progress"
# v1.13.0 fix (B1): .findings outer-wrapper bevisst fjernet — DS' .findings er
# grid 360px+1fr (list+detail-panel), men playground bruker bare list-kolonnen.
# Asserterer .findings__list (BEM-list) i stedet for å bekrefte at findings-
# strukturen fortsatt er i bruk uten at vi misbruker grid-containeren.
DS_CLASSES=".pyramide .matrix .radar .findings__list .distribution .critique-card .capability-matrix .aiact-timeline .tracks .error-summary .guide-panel .expansion .form-progress"
for cls in $DS_CLASSES; do
# Match som klassen i class="..." eller selektor — søk på klassenavnet uten leading dot
bare="${cls#.}"