feat(shared): add Playground design system v0.1 with Tier 1+2 components

Aksel/Digdir-aligned design system for plugin Playgrounds — visual self-service
UIs that complement terminal slash-commands. Targets ms-ai-architect, okr,
llm-security, ultraplan-local, config-audit. Built for Norwegian public sector
decision-makers plus developer power-users — one visual family, two info
densities.

Generated by claude.ai/design (Anthropic) in a dialog-based design session
driven by a comprehensive brief covering all five target plugins, Aksel/Digdir
conventions, and domain-specific visual standards (NS 5814 ROS matrices, EU AI
Act 4-tier pyramide, Doerr OKR scoring, NIST CSF, OWASP threat modeling).
Per Anthropic Consumer Terms §4, ownership of outputs is assigned to the user;
licensed MIT.

shared/playground-design-system/ (5874 lines CSS + JSON):
- tokens.css: Inter font, Digdir blue #0062BA, deuteranopia-safe severity ramp,
  distinct severity-red (#A40E26) vs failure-red (#7D1A1A), plugin scope colors,
  light + dark themes
- base.css: reset, typography (17px body, 65ch measure), focus rings, buttons,
  badges, forms, Aksel 3-tier inline messages, prefers-reduced-motion support
- components.css: Tier 1 — radar/spider, 5x5 matrix-heatmap (bottom-left
  origin, ROS/DPIA), findings-browser, critique-card, wizard/stepper,
  live-meter with antipattern lints
- components-tier2.css: Tier 2 — decision-tree, traffic-lights with rationale,
  diff-review, treemap, distribution P10/P50/P90, command-pipeline output, AI
  Act 4-color pyramide, pipeline-cockpit, verdict-pill + 5-band risk-meter,
  codepoint-reveal (Unicode steg), small-multiples grid (16-cat posture),
  OWASP badges (LLM/ASI/AST/MCP)
- print.css: A4 stylesheet with BW severity hatching, kommune-logo slot,
  signature lines for offentlige dokumenter
- schemas/: finding.schema.json, okr-set.schema.json, ros-threat.schema.json
- README.md: usage guide, design principles, component reference, provenance

shared/playground-examples/:
- index.html: system showcase with all components live
- ros-lier-kommune.html: Lier kommune Copilot ROS-rapport (Scenario A)
- okr-baerum.html: Baerum kommune T2-2026 OKR live writer (Scenario B)
- security-vegvesen.html: SVV ToxicSkills findings review, 85 funn BLOCK
  (Scenario C)
- templates.html: A4 print template demos
- ros-app.js + ros-data.js: Scenario A interactivity

WCAG 2.1 AA throughout (UU-loven krav for offentlig sektor): focus rings, ARIA
attributes, keyboard navigation, severity numerical redundancy for deuteranopia
and BW print, semantic HTML.

Known limitation: Inter loaded via Google Fonts CDN violates self-contained
no-CDN constraint. System-stack fallback works offline. Self-host woff2 files
in Phase 2.
This commit is contained in:
Kjell Tore Guttormsen 2026-05-02 06:59:19 +02:00
commit 4a2bf3567a
16 changed files with 6065 additions and 0 deletions

View file

@ -0,0 +1,465 @@
<!doctype html>
<html lang="nb">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Templates · Playground Design System</title>
<link rel="stylesheet" href="../playground-design-system/tokens.css" />
<link rel="stylesheet" href="../playground-design-system/base.css" />
<link rel="stylesheet" href="../playground-design-system/components.css" />
<link rel="stylesheet" href="../playground-design-system/components-tier2.css" />
<link rel="stylesheet" href="../playground-design-system/print.css" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
.page { padding: var(--space-8) 0 var(--space-16); }
.tpl-grid { display: grid; grid-template-columns: 240px 1fr; gap: var(--space-8); align-items: start; }
.tpl-nav { position: sticky; top: var(--space-4); display: flex; flex-direction: column; gap: 2px; }
.tpl-nav a {
padding: 8px 12px; font-size: var(--font-size-sm);
color: var(--color-text-secondary); text-decoration: none;
border-radius: var(--radius-sm); border-left: 2px solid transparent;
}
.tpl-nav a:hover { background: var(--color-bg-soft); color: var(--color-text-primary); }
.tpl-nav a[aria-current="true"] { background: var(--color-primary-50); color: var(--color-primary-700); border-left-color: var(--color-primary-500); font-weight: var(--font-weight-medium); }
.tpl-nav__heading { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); padding: 6px 12px; margin-top: var(--space-3); }
.tpl-nav__heading:first-child { margin-top: 0; }
.tpl { margin-bottom: var(--space-12); padding-bottom: var(--space-8); border-bottom: 1px solid var(--color-border-subtle); }
.tpl__head { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: var(--space-4); }
.tpl__title { display: flex; flex-direction: column; gap: 4px; }
.tpl__title h2 { font-size: var(--font-size-2xl); margin: 0; }
.tpl__eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-text-tertiary); font-weight: var(--font-weight-semibold); }
.tpl__lede { color: var(--color-text-secondary); font-size: var(--font-size-sm); margin-top: 4px; max-width: var(--measure); }
.tpl__demo {
background: var(--color-bg-soft);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
padding: var(--space-6);
margin-bottom: var(--space-3);
}
.tpl__code {
background: #1F2328; color: #E6E6E6;
border-radius: var(--radius-md);
padding: var(--space-4);
font-family: var(--font-family-mono);
font-size: 12px;
line-height: 1.55;
overflow-x: auto;
white-space: pre;
max-height: 320px;
overflow-y: auto;
}
.tpl__copy {
display: flex; justify-content: space-between; align-items: center;
padding: 6px 10px;
background: #2A2F36; color: #C2C8D0;
font-size: 11px; font-family: var(--font-family-mono);
border-radius: var(--radius-md) var(--radius-md) 0 0;
margin-bottom: 0;
}
.tpl__copy + .tpl__code { border-radius: 0 0 var(--radius-md) var(--radius-md); }
.tpl__copy button {
background: transparent; border: 1px solid #3A3F47; color: #C2C8D0;
padding: 3px 8px; border-radius: 3px; font-size: 11px; cursor: pointer; font-family: inherit;
}
.tpl__copy button:hover { background: #3A3F47; }
.pg-print-preview-banner {
background: var(--color-bg-soft); border: 1px solid var(--color-border-subtle); padding: var(--space-3) var(--space-4);
border-radius: var(--radius-md); display: flex; gap: var(--space-3); align-items: center;
margin-bottom: var(--space-4); font-size: var(--font-size-sm);
}
/* A4-preview emulator (skjerm) */
.a4-preview {
background: #ddd;
padding: var(--space-6);
border-radius: var(--radius-md);
overflow: auto;
}
.a4-page {
width: 210mm;
min-height: 297mm;
margin: 0 auto;
background: #fff;
padding: 22mm 18mm;
box-shadow: 0 6px 24px rgba(0,0,0,0.18);
font-family: "Inter", sans-serif;
font-size: 11pt;
line-height: 1.45;
color: #000;
transform: scale(0.72); transform-origin: top center;
}
@media (max-width: 980px) {
.tpl-grid { grid-template-columns: 1fr; }
.tpl-nav { position: static; }
}
</style>
</head>
<body>
<header class="app-header">
<a href="index.html" class="app-header__brand">
<span class="app-header__brand-mark">P</span>
<span>Playground Design System</span>
</a>
<span class="app-header__breadcrumb"><span aria-hidden="true">/</span> Templates</span>
<span class="app-header__spacer"></span>
<a href="index.html" class="btn btn--ghost btn--sm">← Til oversikt</a>
</header>
<main class="container container--wide page">
<div class="tpl-grid">
<!-- NAV -->
<nav class="tpl-nav" aria-label="Templates">
<span class="tpl-nav__heading">HTML-startere</span>
<a href="#skeleton" aria-current="true">Skeleton</a>
<a href="#intake-wizard">Intake-wizard</a>
<a href="#single-report">Single-report</a>
<a href="#findings-review">Findings-review</a>
<a href="#live-writer">Live-writer</a>
<span class="tpl-nav__heading">Print &amp; data</span>
<a href="#a4-print">A4-rapport (print)</a>
<a href="#schemas">JSON-skjemaer</a>
</nav>
<!-- CONTENT -->
<div>
<div style="margin-bottom: var(--space-8);">
<span style="font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-text-tertiary); font-weight: var(--font-weight-semibold);">Fase 3 · Templates</span>
<h1 style="margin: 4px 0 8px; font-size: var(--font-size-3xl);">Copy-paste startere for nye plugins</h1>
<p style="color: var(--color-text-secondary); max-width: 65ch; font-size: var(--font-size-md);">
Hver template er minst mulig HTML som korrekt importerer designsystemet og bruker etablerte mønstre.
Forke en plugin? Start fra én av disse, ikke fra blank fil.
</p>
</div>
<!-- ============================================== -->
<!-- SKELETON -->
<!-- ============================================== -->
<section class="tpl" id="skeleton">
<div class="tpl__head">
<div class="tpl__title">
<span class="tpl__eyebrow">Template 01</span>
<h2>Skeleton — minimal HTML-side</h2>
<p class="tpl__lede">Bare designsystemet importert. Container, header, og en tom <code>main</code>. Bruk når du vil bygge noe helt eget med tokens og base-styling.</p>
</div>
<span class="badge badge--soft">~ 30 linjer</span>
</div>
<div class="tpl__copy"><span>scenarios/&lt;ditt-scenario&gt;.html</span><button onclick="copyCode(this)">Kopier</button></div>
<pre class="tpl__code">&lt;!doctype html&gt;
&lt;html lang="nb"&gt;
&lt;head&gt;
&lt;meta charset="utf-8" /&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1" /&gt;
&lt;title&gt;Min plugin — &lt;org&gt;&lt;/title&gt;
&lt;link rel="stylesheet" href="../playground-design-system/tokens.css" /&gt;
&lt;link rel="stylesheet" href="../playground-design-system/base.css" /&gt;
&lt;link rel="stylesheet" href="../playground-design-system/components.css" /&gt;
&lt;link rel="stylesheet" href="../playground-design-system/components-tier2.css" /&gt;
&lt;link rel="stylesheet" href="../playground-design-system/print.css" /&gt;
&lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt;
&lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=JetBrains+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;header class="app-header"&gt;
&lt;a href="index.html" class="app-header__brand"&gt;
&lt;span class="app-header__brand-mark"&gt;P&lt;/span&gt;
&lt;span&gt;Min plugin&lt;/span&gt;
&lt;/a&gt;
&lt;span class="app-header__breadcrumb"&gt;/ &lt;org&gt;&lt;/span&gt;
&lt;/header&gt;
&lt;main class="container container--wide" style="padding: var(--space-8) 0;"&gt;
&lt;h1&gt;Tittel&lt;/h1&gt;
&lt;p class="text-secondary"&gt;Innhold her.&lt;/p&gt;
&lt;/main&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
</section>
<!-- ============================================== -->
<!-- INTAKE WIZARD -->
<!-- ============================================== -->
<section class="tpl" id="intake-wizard">
<div class="tpl__head">
<div class="tpl__title">
<span class="tpl__eyebrow">Template 02</span>
<h2>Intake-wizard</h2>
<p class="tpl__lede">Fire-stegs onboarding. Sticky stepper, valideringsgate framover, localStorage-persistens. Brukes for ROS-intake, OKR-onboarding, security-clean.</p>
</div>
<span class="badge badge--soft">scenarios/ros-lier-kommune.html (skjerm 1)</span>
</div>
<div class="tpl__demo">
<nav class="stepper" style="margin-bottom: 0; border-bottom: none; padding-bottom: 0;">
<button type="button" class="stepper__step" data-state="complete">
<span class="stepper__step-number"><span class="stepper__step-number-text">1</span></span>
<span class="stepper__step-text"><span class="stepper__step-label">Org-profil</span><span class="stepper__step-hint">Ferdig</span></span>
</button>
<button type="button" class="stepper__step" data-state="active">
<span class="stepper__step-number"><span class="stepper__step-number-text">2</span></span>
<span class="stepper__step-text"><span class="stepper__step-label">System</span><span class="stepper__step-hint">Pågår</span></span>
</button>
<button type="button" class="stepper__step" data-state="pending">
<span class="stepper__step-number"><span class="stepper__step-number-text">3</span></span>
<span class="stepper__step-text"><span class="stepper__step-label">Compliance</span><span class="stepper__step-hint">Venter</span></span>
</button>
<button type="button" class="stepper__step" data-state="pending">
<span class="stepper__step-number"><span class="stepper__step-number-text">4</span></span>
<span class="stepper__step-text"><span class="stepper__step-label">Bekreft</span><span class="stepper__step-hint">Venter</span></span>
</button>
</nav>
</div>
<p style="font-size: 12px; color: var(--color-text-tertiary); font-family: var(--font-family-mono);">→ Se <a href="../scenarios/ros-lier-kommune.html#intake">ros-lier-kommune.html#intake</a> for full implementasjon med skjema-felt og validering.</p>
</section>
<!-- ============================================== -->
<!-- SINGLE REPORT -->
<!-- ============================================== -->
<section class="tpl" id="single-report">
<div class="tpl__head">
<div class="tpl__title">
<span class="tpl__eyebrow">Template 03</span>
<h2>Single-report</h2>
<p class="tpl__lede">Én rapport, fire seksjoner: header med metadata + verdict-pill, hovedinnhold, sidefelt, signatur. Bygd for projector-bruk og PDF-eksport.</p>
</div>
<span class="badge badge--soft">scenarios/security-vegvesen.html</span>
</div>
<div class="tpl__demo" style="background: #fff; padding: var(--space-5);">
<div style="display: grid; grid-template-columns: 1fr auto; gap: var(--space-5); align-items: start; padding-bottom: var(--space-4); border-bottom: 1px solid var(--color-border-subtle);">
<div>
<span style="font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-scope-architect); font-weight: var(--font-weight-semibold);">Eyebrow · scope</span>
<h3 style="margin: 4px 0 6px; font-size: var(--font-size-xl);">Rapporttittel</h3>
<div style="display: flex; gap: var(--space-3); font-size: var(--font-size-sm); color: var(--color-text-secondary);">
<span><span style="font-size: 11px; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em;">Eier</span> Person</span>
<span><span style="font-size: 11px; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em;">Dato</span> 02. mai</span>
</div>
</div>
<span class="verdict-pill-lg" data-verdict="warning" style="padding: 8px 14px;">
<span class="verdict-pill-lg__verdict" style="font-size: var(--font-size-md);">WARN</span>
<span class="verdict-pill-lg__sub">Manuell gjennomgang</span>
</span>
</div>
<div style="display: grid; grid-template-columns: 1fr 220px; gap: var(--space-5); margin-top: var(--space-4);">
<div>
<h4 style="font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); margin: 0 0 8px;">Sammendrag</h4>
<p style="font-size: var(--font-size-sm); color: var(--color-text-secondary); margin: 0;">Hovedinnhold går her — typisk 2-4 avsnitt med mellomtitler.</p>
</div>
<aside style="padding-left: var(--space-4); border-left: 1px solid var(--color-border-subtle); font-size: 12px; color: var(--color-text-secondary);">
<strong>Kort fakta</strong><br>
Sidekontekst, henvisninger, neste-steg.
</aside>
</div>
</div>
</section>
<!-- ============================================== -->
<!-- FINDINGS REVIEW -->
<!-- ============================================== -->
<section class="tpl" id="findings-review">
<div class="tpl__head">
<div class="tpl__title">
<span class="tpl__eyebrow">Template 04</span>
<h2>Findings-review</h2>
<p class="tpl__lede">Posture-grid + filter-bar + finding-kort + tiltaksplan. Strukturen i Scenario C i konsentrert form.</p>
</div>
<span class="badge badge--soft">scenarios/security-vegvesen.html</span>
</div>
<div class="tpl__demo" style="background: var(--color-surface);">
<div class="filter-bar" style="margin-bottom: var(--space-4);">
<div class="filter-bar__group">
<span class="filter-bar__label">Alvorlighet</span>
<button class="chip" aria-pressed="true">Alle <span class="chip__count">12</span></button>
<button class="chip" aria-pressed="false">Kritisk <span class="chip__count">2</span></button>
<button class="chip" aria-pressed="false">Høy <span class="chip__count">3</span></button>
</div>
</div>
<article class="finding" data-sev="high" style="margin-bottom: 0;">
<header class="finding__head">
<div>
<div class="finding__id">PROJEKT-123 · F-001</div>
<h3 class="finding__title" style="font-size: var(--font-size-md);">Funn-tittel</h3>
</div>
<div></div>
<div class="finding__badges">
<span class="rule-badge badge--owasp-llm">RULE01</span>
<span class="badge" style="background: var(--color-severity-high); color: #fff;">Høy</span>
</div>
</header>
<div class="finding__body" style="padding: var(--space-3) var(--space-4);">
<p style="margin: 0; font-size: var(--font-size-sm); color: var(--color-text-secondary);">Kort beskrivelse av funnet. Full struktur med kildekontekst, anbefaling og side-felt finnes i Scenario C.</p>
</div>
</article>
</div>
</section>
<!-- ============================================== -->
<!-- LIVE WRITER -->
<!-- ============================================== -->
<section class="tpl" id="live-writer">
<div class="tpl__head">
<div class="tpl__title">
<span class="tpl__eyebrow">Template 05</span>
<h2>Live-writer</h2>
<p class="tpl__lede">To-pane: editor med inline highlights til venstre, kritikk-stack til høyre. Score-strip øverst. Fire view-modi: skriv / sammenlign / kohort / endelig.</p>
</div>
<span class="badge badge--soft">scenarios/okr-baerum.html</span>
</div>
<div class="tpl__demo" style="padding: var(--space-4);">
<div style="display: grid; grid-template-columns: 1.4fr 1fr; gap: var(--space-4);">
<div style="background: #fff; border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); padding: var(--space-3);">
<div style="font-size: 11px; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 6px;">Editor</div>
<p style="margin: 0; font-family: var(--font-family-serif); font-size: 16px; line-height: 1.55;">
Innhold med <span style="background-image: linear-gradient(transparent 60%, rgba(204,90,0,0.22) 60%); border-bottom: 2px solid var(--color-severity-high); padding-bottom: 1px;">inline highlight</span> som lenker til kritikk-kortet til høyre.
</p>
</div>
<div style="background: #fff; border: 1px solid var(--color-primary-500); border-radius: var(--radius-md); padding: var(--space-3); box-shadow: 0 0 0 2px var(--color-primary-100);">
<div style="display: flex; gap: 8px; align-items: center; margin-bottom: 4px;">
<span style="width: 8px; height: 8px; border-radius: 50%; background: var(--color-severity-high);"></span>
<strong style="font-size: var(--font-size-sm);">Kritikk-tittel</strong>
</div>
<p style="margin: 0 0 8px; font-size: 12px; color: var(--color-text-secondary); line-height: 1.5;">Kort forklaring og forslag til omskriving.</p>
<button class="btn btn--primary btn--sm">Bruk forslag</button>
</div>
</div>
</div>
</section>
<!-- ============================================== -->
<!-- A4 PRINT -->
<!-- ============================================== -->
<section class="tpl" id="a4-print">
<div class="tpl__head">
<div class="tpl__title">
<span class="tpl__eyebrow">Template 06 · Print</span>
<h2>A4-rapport · offentlig dokument</h2>
<p class="tpl__lede">Skraverings-mønstre i stedet for farge for B/W-utskrift. Header med kommune-logo-slot og signaturfelt. Importer <code>print.css</code> og legg innhold i en <code>.a4</code>-wrapper for skjerm-preview.</p>
</div>
<button class="btn btn--secondary btn--sm" onclick="window.print()">Skriv ut nå</button>
</div>
<div class="pg-print-preview-banner">
<span style="font-size: 18px;">📄</span>
<span>Slik ser dokumentet ut på A4. <kbd style="background: var(--color-bg-soft); border: 1px solid var(--color-border-moderate); border-radius: 3px; padding: 1px 5px; font-family: var(--font-family-mono); font-size: 11px;">Cmd/Ctrl + P</kbd> for ekte print-preview.</span>
</div>
<div class="a4-preview">
<div class="a4-page" id="a4-demo">
<div class="print-header" style="display: grid; grid-template-columns: auto 1fr; gap: 14pt; align-items: center; padding-bottom: 10pt; margin-bottom: 16pt; border-bottom: 0.5pt solid #888;">
<div class="print-header__logo" style="width: 40pt; height: 40pt; border: 0.5pt solid #888; display: flex; align-items: center; justify-content: center; font-size: 9pt; color: #888;">[logo]</div>
<div>
<div style="font-size: 9pt; color: #555; text-transform: uppercase; letter-spacing: 0.06em;">Lier kommune · IT-styring</div>
<div style="font-size: 14pt; font-weight: 600; margin: 2pt 0;">Risiko- og sårbarhetsanalyse · M365 Copilot</div>
<div style="font-size: 9pt; color: #555;"><strong>ROS-2026-LIER-COPILOT-01</strong> · 02. mai 2026 · Eier: Eli Bjerke</div>
</div>
</div>
<h2 style="font-size: 12pt; margin: 12pt 0 4pt;">Sammendrag</h2>
<p style="font-size: 10pt; line-height: 1.45; margin: 0 0 8pt;">M365 Copilot foreslås innført for 1 850 ansatte. Analysen identifiserte 49 trusler, hvorav 4 ligger i kritisk sone og 12 i høy sone før mitigerende tiltak. Anbefalingen er <strong>GO med fire betingelser</strong> beskrevet i kap. 6.</p>
<h2 style="font-size: 12pt; margin: 12pt 0 4pt;">Risiko-matrise (5×5)</h2>
<table style="border-collapse: collapse; width: 100%; font-size: 9pt; margin-bottom: 8pt;">
<thead>
<tr><th style="padding: 4pt; border-bottom: 0.5pt solid #000; text-align: left;">Sone</th><th style="padding: 4pt; border-bottom: 0.5pt solid #000;">Mønster</th><th style="padding: 4pt; border-bottom: 0.5pt solid #000;">Antall trusler</th></tr>
</thead>
<tbody>
<tr><td style="padding: 4pt;">Lav (14)</td><td style="padding: 4pt;"><span style="display: inline-block; width: 18pt; height: 10pt; background: #fff; border: 0.5pt solid #000; vertical-align: middle;"></span></td><td style="padding: 4pt;">21</td></tr>
<tr><td style="padding: 4pt;">Moderat (58)</td><td style="padding: 4pt;"><span style="display: inline-block; width: 18pt; height: 10pt; background: repeating-linear-gradient(45deg, #000 0 0.5pt, transparent 0.5pt 4pt); border: 0.5pt solid #000; vertical-align: middle;"></span></td><td style="padding: 4pt;">12</td></tr>
<tr><td style="padding: 4pt;">Høy (912)</td><td style="padding: 4pt;"><span style="display: inline-block; width: 18pt; height: 10pt; background: repeating-linear-gradient(45deg, #000 0 0.7pt, transparent 0.7pt 3pt); border: 0.5pt solid #000; vertical-align: middle;"></span></td><td style="padding: 4pt;">12</td></tr>
<tr><td style="padding: 4pt;">Kritisk (1520)</td><td style="padding: 4pt;"><span style="display: inline-block; width: 18pt; height: 10pt; background: repeating-linear-gradient(45deg, #000 0 1pt, transparent 1pt 2pt); border: 0.5pt solid #000; vertical-align: middle;"></span></td><td style="padding: 4pt;">3</td></tr>
<tr><td style="padding: 4pt;">Ekstrem (25)</td><td style="padding: 4pt;"><span style="display: inline-block; width: 18pt; height: 10pt; background: #000; border: 0.5pt solid #000; vertical-align: middle;"></span></td><td style="padding: 4pt;">1</td></tr>
</tbody>
</table>
<h2 style="font-size: 12pt; margin: 12pt 0 4pt;">Anbefaling</h2>
<p style="font-size: 10pt; line-height: 1.45; margin: 0 0 8pt;">GO med fire betingelser: (1) DLP-policy aktivert i tenant før utrulling. (2) Sensitivity Labels innført i alle arkivsystem. (3) Schrems II-vurdering ferdigstilt for cross-tenant. (4) Innbygger-tilfredshetsmåling baseline T1.</p>
<div class="print-footer" style="margin-top: 24pt; padding-top: 10pt; border-top: 0.5pt solid #888; display: grid; grid-template-columns: 1fr 1fr; gap: 18pt; font-size: 9pt;">
<div class="print-signature">
<div class="print-signature__line" style="border-bottom: 0.5pt solid #000; height: 28pt;"></div>
<div class="print-signature__caption" style="font-size: 9pt; color: #555;">Eli Bjerke · IT-sikkerhetsleder · dato</div>
</div>
<div class="print-signature">
<div class="print-signature__line" style="border-bottom: 0.5pt solid #000; height: 28pt;"></div>
<div class="print-signature__caption" style="font-size: 9pt; color: #555;">Kommunaldirektør · dato</div>
</div>
</div>
</div>
</div>
</section>
<!-- ============================================== -->
<!-- SCHEMAS -->
<!-- ============================================== -->
<section class="tpl" id="schemas">
<div class="tpl__head">
<div class="tpl__title">
<span class="tpl__eyebrow">Datakontrakter</span>
<h2>JSON-skjemaer</h2>
<p class="tpl__lede">Tre skjemaer som lar plugins utveksle data uten gjetting. Validér med vanilig <code>ajv</code> eller VS Codes innebygde schema-validator.</p>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--space-3);">
<a class="card" style="text-decoration: none; color: inherit; display: flex; flex-direction: column; gap: 6px;" href="../playground-design-system/schemas/finding.schema.json">
<span style="font-size: 11px; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em;">Schema</span>
<strong>finding.schema.json</strong>
<span style="font-size: 12px; color: var(--color-text-secondary);">Ett funn fra en skanning. severity, source, evidence, recommendation, status.</span>
<span style="font-family: var(--font-family-mono); font-size: 11px; color: var(--color-text-tertiary); margin-top: auto;">llm-security · config-audit · ms-ai-review · ultraplan-review</span>
</a>
<a class="card" style="text-decoration: none; color: inherit; display: flex; flex-direction: column; gap: 6px;" href="../playground-design-system/schemas/okr-set.schema.json">
<span style="font-size: 11px; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em;">Schema</span>
<strong>okr-set.schema.json</strong>
<span style="font-size: 12px; color: var(--color-text-secondary);">Objective + 16 nøkkelresultater. baseline/target/stretch, period, score, critiques.</span>
<span style="font-family: var(--font-family-mono); font-size: 11px; color: var(--color-text-tertiary); margin-top: auto;">OKR live-writer</span>
</a>
<a class="card" style="text-decoration: none; color: inherit; display: flex; flex-direction: column; gap: 6px;" href="../playground-design-system/schemas/ros-threat.schema.json">
<span style="font-size: 11px; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em;">Schema</span>
<strong>ros-threat.schema.json</strong>
<span style="font-size: 12px; color: var(--color-text-secondary);">NS 5814-justert trussel. inherent + residual, controls (M-001…), regulatory_refs.</span>
<span style="font-family: var(--font-family-mono); font-size: 11px; color: var(--color-text-tertiary); margin-top: auto;">ms-ai-architect</span>
</a>
</div>
<p style="font-size: 12px; color: var(--color-text-tertiary); margin-top: var(--space-4);">Bruk i HTML/JS: <code>fetch('/shared/playground-design-system/schemas/finding.schema.json').then(r =&gt; r.json())</code></p>
</section>
</div>
</div>
</main>
<script>
// smooth nav
document.querySelectorAll('.tpl-nav a').forEach(a => {
a.addEventListener('click', () => {
document.querySelectorAll('.tpl-nav a').forEach(x => x.removeAttribute('aria-current'));
a.setAttribute('aria-current', 'true');
});
});
function copyCode(btn) {
const code = btn.closest('.tpl').querySelector('.tpl__code').textContent;
navigator.clipboard?.writeText(code).then(() => {
const orig = btn.textContent;
btn.textContent = 'Kopiert!';
setTimeout(() => { btn.textContent = orig; }, 1400);
});
}
</script>
</body>
</html>