ktg-plugin-marketplace/shared/playground-examples/okr-baerum.html
Kjell Tore Guttormsen 4a2bf3567a 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.
2026-05-02 06:59:19 +02:00

868 lines
44 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="nb">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OKR live-writer — Bærum kommune — T2 2026</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="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&family=Source+Serif+4:wght@400;600&display=swap" rel="stylesheet">
<style>
.layout { display: grid; grid-template-rows: auto 1fr; min-height: 100vh; }
.page { padding: var(--space-8) 0 var(--space-16); }
.page__header {
display: flex; justify-content: space-between; align-items: flex-end;
gap: var(--space-6); margin-bottom: var(--space-6);
border-bottom: 1px solid var(--color-border-subtle);
padding-bottom: var(--space-4);
}
.page__title { display: flex; flex-direction: column; gap: 4px; }
.page__eyebrow {
font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.1em;
color: var(--color-scope-okr); font-weight: var(--font-weight-semibold);
}
.page__meta { display: flex; gap: var(--space-4); font-size: var(--font-size-sm); color: var(--color-text-secondary); }
.page__meta-item { display: flex; gap: 6px; align-items: baseline; }
.page__meta-label { color: var(--color-text-tertiary); font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.06em; }
/* Two-pane writer layout */
.writer {
display: grid;
grid-template-columns: 1.4fr 1fr;
gap: var(--space-6);
align-items: start;
}
.pane {
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
overflow: hidden;
}
.pane__head {
padding: 10px 16px;
background: var(--color-bg-soft);
border-bottom: 1px solid var(--color-border-subtle);
display: flex; justify-content: space-between; align-items: center;
gap: var(--space-3);
}
.pane__title {
font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold);
color: var(--color-text-primary); margin: 0;
display: flex; align-items: center; gap: 8px;
}
.pane__title-eyebrow {
font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
color: var(--color-text-tertiary); font-weight: var(--font-weight-medium);
}
.pane__body { padding: var(--space-5); }
/* Editor styling */
.editor {
font-family: var(--font-family-serif);
font-size: 18px;
line-height: 1.7;
min-height: 380px;
outline: none;
}
.editor h2 {
font-family: var(--font-family-sans);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
margin: 0 0 var(--space-2);
color: var(--color-text-primary);
}
.editor .objective {
font-family: var(--font-family-serif);
font-size: 22px;
font-weight: 600;
line-height: 1.35;
margin-bottom: var(--space-5);
color: var(--color-text-primary);
}
.editor .kr {
margin: 0 0 var(--space-4);
padding: var(--space-3) var(--space-4);
background: var(--color-bg-soft);
border-radius: var(--radius-sm);
border-left: 3px solid var(--color-scope-okr);
position: relative;
}
.editor .kr-label {
font-family: var(--font-family-mono);
font-size: 11px;
color: var(--color-scope-okr);
font-weight: var(--font-weight-semibold);
letter-spacing: 0.06em;
margin-bottom: 4px;
display: block;
}
.editor .kr-text { font-family: var(--font-family-serif); font-size: 18px; line-height: 1.5; }
/* Inline highlight overlays in the editor */
.hl {
background-image: linear-gradient(to bottom, transparent 0, transparent 60%, var(--hl-color, rgba(191,135,0,0.25)) 60%, var(--hl-color, rgba(191,135,0,0.25)) 100%);
cursor: help;
border-bottom: 2px solid var(--hl-border, var(--color-severity-medium));
padding-bottom: 1px;
}
.hl[data-issue="missing-baseline"] { --hl-color: rgba(191,135,0,0.22); --hl-border: var(--color-severity-medium); }
.hl[data-issue="vague-verb"] { --hl-color: rgba(204,90,0,0.22); --hl-border: var(--color-severity-high); }
.hl[data-issue="activity"] { --hl-color: rgba(164,14,38,0.18); --hl-border: var(--color-severity-critical); }
.hl[data-issue="no-deadline"] { --hl-color: rgba(191,135,0,0.22); --hl-border: var(--color-severity-medium); }
.hl[data-issue="no-metric"] { --hl-color: rgba(204,90,0,0.22); --hl-border: var(--color-severity-high); }
/* Score header */
.score-strip {
display: grid;
grid-template-columns: auto 1fr auto;
gap: var(--space-5);
align-items: center;
padding: var(--space-5);
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
margin-bottom: var(--space-5);
}
.score-strip__num {
font-size: 48px;
font-weight: var(--font-weight-bold);
line-height: 1;
font-variant-numeric: tabular-nums;
letter-spacing: -0.02em;
color: var(--color-text-primary);
}
.score-strip__num small { font-size: 18px; color: var(--color-text-tertiary); font-weight: var(--font-weight-medium); }
.score-strip__bars { display: flex; flex-direction: column; gap: 6px; }
.score-strip__bar { display: grid; grid-template-columns: 70px 1fr 36px; gap: 8px; align-items: center; font-size: 12px; }
.score-strip__bar-label { color: var(--color-text-secondary); font-family: var(--font-family-mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; }
.score-strip__bar-track { height: 6px; background: var(--color-surface-sunken); border-radius: var(--radius-pill); overflow: hidden; }
.score-strip__bar-fill { height: 100%; border-radius: var(--radius-pill); }
.score-strip__bar-num { font-family: var(--font-family-mono); font-variant-numeric: tabular-nums; color: var(--color-text-secondary); text-align: right; }
/* Live update indicator */
.live-dot {
display: inline-flex; align-items: center; gap: 6px;
font-size: 11px; color: var(--color-text-tertiary);
font-family: var(--font-family-mono); text-transform: uppercase; letter-spacing: 0.06em;
}
.live-dot__pulse {
width: 6px; height: 6px; border-radius: 50%;
background: var(--color-state-success);
box-shadow: 0 0 0 0 currentColor;
animation: pulse 1.6s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(26,127,55,0.4); }
70% { box-shadow: 0 0 0 6px rgba(26,127,55,0); }
100% { box-shadow: 0 0 0 0 rgba(26,127,55,0); }
}
/* Critique stack */
.critiques { display: flex; flex-direction: column; gap: var(--space-3); }
.critique {
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
background: var(--color-surface);
overflow: hidden;
transition: border-color 0.15s, box-shadow 0.15s;
}
.critique:hover { border-color: var(--color-border-moderate); box-shadow: var(--shadow-sm); }
.critique[data-active="true"] {
border-color: var(--color-primary-500);
box-shadow: 0 0 0 2px var(--color-primary-100);
}
.critique__head {
display: grid; grid-template-columns: auto 1fr auto;
gap: var(--space-3);
padding: 12px 14px;
align-items: center;
cursor: pointer;
}
.critique__sev {
width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
}
.critique[data-severity="high"] .critique__sev { background: var(--color-severity-high); }
.critique[data-severity="medium"] .critique__sev { background: var(--color-severity-medium); }
.critique[data-severity="low"] .critique__sev { background: var(--color-severity-low); }
.critique[data-severity="info"] .critique__sev { background: var(--color-state-info); }
.critique__title { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); }
.critique__meta {
display: flex; gap: 6px; font-size: 11px;
font-family: var(--font-family-mono); color: var(--color-text-tertiary);
}
.critique__body {
padding: 0 14px 14px 30px;
display: flex; flex-direction: column; gap: 10px;
font-size: var(--font-size-sm);
line-height: 1.5;
color: var(--color-text-secondary);
}
.critique__quote {
padding: 8px 12px;
background: var(--color-bg-soft);
border-left: 2px solid var(--color-border-moderate);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
font-family: var(--font-family-serif);
font-size: var(--font-size-sm);
color: var(--color-text-primary);
font-style: italic;
}
.critique__suggestion {
padding: 10px 12px;
background: var(--color-severity-low-soft);
color: var(--color-severity-low-on);
border-radius: var(--radius-sm);
font-family: var(--font-family-serif);
font-size: var(--font-size-sm);
line-height: 1.5;
}
.critique__suggestion::before {
content: "→ Forslag: ";
font-family: var(--font-family-sans);
font-weight: var(--font-weight-semibold);
font-style: normal;
}
.critique__actions { display: flex; gap: 8px; padding-top: 4px; }
.critique__rule {
font-size: 11px; font-family: var(--font-family-mono);
color: var(--color-text-tertiary);
padding: 2px 6px;
background: var(--color-surface-sunken);
border-radius: var(--radius-sm);
}
/* Compare mode */
.compare-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
}
.compare-col { padding: var(--space-4); }
.compare-col + .compare-col { border-left: 1px solid var(--color-border-subtle); }
.compare-col__label {
font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
color: var(--color-text-tertiary); margin-bottom: 8px; font-weight: var(--font-weight-semibold);
}
/* Section headers */
.h3 { font-size: var(--font-size-md); font-weight: var(--font-weight-semibold); margin: 0 0 var(--space-3); color: var(--color-text-primary); }
.h4 { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); margin: 0 0 var(--space-2); color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: 0.06em; }
/* Terminology drawer */
.term-drawer {
margin-top: var(--space-6);
padding: var(--space-5);
background: var(--color-bg-soft);
border-radius: var(--radius-md);
border: 1px solid var(--color-border-subtle);
}
.term-row { display: grid; grid-template-columns: 200px 1fr; gap: var(--space-3); padding: 8px 0; border-top: 1px dashed var(--color-border-subtle); font-size: var(--font-size-sm); }
.term-row:first-of-type { border-top: none; }
.term-row dt { font-weight: var(--font-weight-semibold); color: var(--color-text-primary); }
.term-row dd { margin: 0; color: var(--color-text-secondary); line-height: 1.5; }
/* Toggle for view modes */
.view-toggle {
display: flex; gap: 2px; padding: 3px;
background: var(--color-bg-soft); border-radius: var(--radius-md);
}
.view-toggle button {
padding: 6px 12px; font-size: 12px; font-weight: var(--font-weight-medium);
background: transparent; border: none; border-radius: var(--radius-sm);
cursor: pointer; color: var(--color-text-secondary); font-family: inherit;
}
.view-toggle button[aria-pressed="true"] {
background: var(--color-surface); color: var(--color-text-primary);
box-shadow: var(--shadow-sm);
}
/* Cohort comparison */
.cohort-grid {
display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--space-4);
margin-top: var(--space-3);
}
.cohort-card {
padding: var(--space-4);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
background: var(--color-surface);
}
.cohort-card__head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--space-3); }
.cohort-card__name { font-weight: var(--font-weight-semibold); font-size: var(--font-size-sm); }
.cohort-card__count { font-size: 11px; color: var(--color-text-tertiary); font-family: var(--font-family-mono); }
.cohort-card__metric { display: flex; align-items: baseline; gap: 4px; margin-bottom: 8px; }
.cohort-card__metric-num { font-size: var(--font-size-2xl); font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.cohort-card__metric-suffix { font-size: var(--font-size-sm); color: var(--color-text-tertiary); }
/* Final summary */
.final-banner {
padding: var(--space-5);
background: var(--color-severity-low-soft);
color: var(--color-severity-low-on);
border-radius: var(--radius-md);
border: 1px solid #BFDDC8;
display: grid; grid-template-columns: auto 1fr auto; gap: var(--space-5);
align-items: center;
margin-bottom: var(--space-5);
}
.final-banner__icon {
width: 44px; height: 44px; border-radius: 50%;
background: var(--color-severity-low); color: #fff;
display: flex; align-items: center; justify-content: center;
font-size: 24px; font-weight: var(--font-weight-bold);
}
@media (max-width: 1100px) {
.writer { grid-template-columns: 1fr; }
.cohort-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="layout">
<!-- HEADER STRIP ============================================ -->
<header style="background: var(--color-surface); border-bottom: 1px solid var(--color-border-subtle); padding: 12px 0;">
<div class="container" style="display: flex; justify-content: space-between; align-items: center;">
<div style="display: flex; align-items: center; gap: var(--space-4);">
<a href="index.html" style="text-decoration: none; color: var(--color-text-tertiary); font-size: var(--font-size-sm);">← Tilbake</a>
<span style="color: var(--color-border-moderate);">/</span>
<span style="font-size: var(--font-size-sm); color: var(--color-text-secondary);">Playground / Scenarios / OKR live writer</span>
</div>
<div style="display: flex; gap: var(--space-3); align-items: center;">
<span class="live-dot"><span class="live-dot__pulse"></span> Live · 4 forfattere</span>
<button class="btn btn--ghost" id="theme-toggle" aria-pressed="false">Mørk</button>
</div>
</div>
</header>
<main class="container page">
<!-- PAGE HEADER -->
<div class="page__header">
<div class="page__title">
<span class="page__eyebrow">OKR live-writer · Bærum kommune</span>
<h1 style="margin: 0; font-size: var(--font-size-3xl);">Tjeneste­utvikling — T2 2026</h1>
<div class="page__meta">
<span class="page__meta-item"><span class="page__meta-label">Avd.</span> Innbyggertjenester</span>
<span class="page__meta-item"><span class="page__meta-label">Eier</span> Anne Hovde</span>
<span class="page__meta-item"><span class="page__meta-label">Frist</span> 15. mai 2026</span>
<span class="page__meta-item"><span class="page__meta-label">Lagret</span> 12 sek siden</span>
</div>
</div>
<div style="display: flex; gap: var(--space-2);">
<button class="btn btn--ghost">Versjoner</button>
<button class="btn btn--secondary">Eksporter PDF</button>
<button class="btn btn--primary">Send til godkjenning</button>
</div>
</div>
<!-- SCORE STRIP -->
<div class="score-strip">
<div class="score-strip__num" id="score-num">62<small>/100</small></div>
<div class="score-strip__bars">
<div class="score-strip__bar">
<span class="score-strip__bar-label">Måling</span>
<div class="score-strip__bar-track"><div class="score-strip__bar-fill" style="width: 40%; background: var(--color-severity-medium);"></div></div>
<span class="score-strip__bar-num">4/10</span>
</div>
<div class="score-strip__bar">
<span class="score-strip__bar-label">Spesifikt</span>
<div class="score-strip__bar-track"><div class="score-strip__bar-fill" style="width: 60%; background: var(--color-severity-high);"></div></div>
<span class="score-strip__bar-num">6/10</span>
</div>
<div class="score-strip__bar">
<span class="score-strip__bar-label">Ambisjon</span>
<div class="score-strip__bar-track"><div class="score-strip__bar-fill" style="width: 70%; background: var(--color-severity-low);"></div></div>
<span class="score-strip__bar-num">7/10</span>
</div>
<div class="score-strip__bar">
<span class="score-strip__bar-label">Påvirkbart</span>
<div class="score-strip__bar-track"><div class="score-strip__bar-fill" style="width: 80%; background: var(--color-severity-low);"></div></div>
<span class="score-strip__bar-num">8/10</span>
</div>
</div>
<div style="display: flex; flex-direction: column; align-items: flex-end; gap: 4px;">
<span class="badge" style="background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on);">Trenger arbeid</span>
<span style="font-size: 11px; color: var(--color-text-tertiary); font-family: var(--font-family-mono);">v0.4 · oppdatert kontinuerlig</span>
</div>
</div>
<!-- VIEW TOGGLE -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-4);">
<div class="view-toggle" role="tablist">
<button role="tab" aria-pressed="true" data-view="writer">Skriv (live-kritikk)</button>
<button role="tab" aria-pressed="false" data-view="rewrite">Sammenlign (før / etter)</button>
<button role="tab" aria-pressed="false" data-view="cohort">Kohort (avd.-gj.snitt)</button>
<button role="tab" aria-pressed="false" data-view="final">Endelig versjon</button>
</div>
<div style="font-size: 11px; color: var(--color-text-tertiary); font-family: var(--font-family-mono);">
Modell kjører lokalt · ingen data forlater Bærum nett
</div>
</div>
<!-- ========================================================= -->
<!-- VIEW 1: WRITER (live critique) -->
<!-- ========================================================= -->
<section class="view" data-view-content="writer">
<div class="writer">
<!-- LEFT: editor -->
<div class="pane">
<div class="pane__head">
<h2 class="pane__title">
<span class="pane__title-eyebrow">Utkast</span>
Tjenesteutvikling — utkast 0.4
</h2>
<span class="live-dot"><span class="live-dot__pulse"></span> Auto-kritikk</span>
</div>
<div class="pane__body">
<div class="editor" id="editor">
<p class="objective">
<span class="hl" data-issue="vague-verb" data-cid="c1">Forbedre</span>
digitale tjenester for innbyggerne i Bærum kommune slik at de
<span class="hl" data-issue="vague-verb" data-cid="c2">opplever bedre service</span>.
</p>
<h2 style="font-size: var(--font-size-sm); color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em;">Nøkkelresultater</h2>
<div class="kr">
<span class="kr-label">KR1</span>
<p class="kr-text">
Øke andelen henvendelser løst i selvbetjeningsløsningen
<span class="hl" data-issue="missing-baseline" data-cid="c3">betydelig</span>
sammenlignet med i fjor.
</p>
</div>
<div class="kr">
<span class="kr-label">KR2</span>
<p class="kr-text">
<span class="hl" data-issue="activity" data-cid="c4">Lansere ny chatbot på kommune.no</span>
innen utgangen av tertialet.
</p>
</div>
<div class="kr">
<span class="kr-label">KR3</span>
<p class="kr-text">
Redusere ventetid for byggesaks­henvendelser
<span class="hl" data-issue="no-metric" data-cid="c5">vesentlig</span>.
</p>
</div>
<div class="kr">
<span class="kr-label">KR4</span>
<p class="kr-text">
Innbygger­tilfredshet på 4,2 av 5 målt i T2-undersøkelsen
<span class="hl" data-issue="no-deadline" data-cid="c6"></span>.
</p>
</div>
</div>
</div>
<div style="padding: 10px 16px; background: var(--color-bg-soft); border-top: 1px solid var(--color-border-subtle); display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: var(--color-text-tertiary); font-family: var(--font-family-mono);">
<span>248 ord · 1 mål · 4 nøkkelresultater</span>
<span>Sist endret 14:23 · Anne H.</span>
</div>
</div>
<!-- RIGHT: critique panel -->
<div class="pane">
<div class="pane__head">
<h2 class="pane__title">
<span class="pane__title-eyebrow">Kritikk</span>
6 funn
</h2>
<span class="badge badge--soft">Regelsett: kommunal-okr-v2</span>
</div>
<div class="pane__body" style="padding: var(--space-3);">
<div class="critiques">
<article class="critique" data-severity="high" data-cid="c4" data-active="true">
<header class="critique__head">
<span class="critique__sev"></span>
<div>
<div class="critique__title">Aktivitet maskert som nøkkelresultat</div>
<div class="critique__meta"><span>KR2</span> · <span class="critique__rule">activity-not-outcome</span></div>
</div>
<span style="font-size: 18px; color: var(--color-text-tertiary);"></span>
</header>
<div class="critique__body">
<div class="critique__quote">«Lansere ny chatbot på kommune.no»</div>
<p>Et nøkkelresultat skal beskrive en <strong>endring i verden</strong>, ikke en aktivitet eller en leveranse. Lansering er en milepæl — det er en input, ikke et utfall.</p>
<div class="critique__suggestion">«Andelen innbyggere som får løst sitt spørsmål i første henvendelse økes fra 38 % (T1 2026) til 55 % innen 31. august 2026.»</div>
<div class="critique__actions">
<button class="btn btn--primary btn--sm">Bruk forslag</button>
<button class="btn btn--ghost btn--sm">Skjul</button>
</div>
</div>
</article>
<article class="critique" data-severity="high" data-cid="c5">
<header class="critique__head">
<span class="critique__sev"></span>
<div>
<div class="critique__title">Ingen målbar verdi</div>
<div class="critique__meta"><span>KR3</span> · <span class="critique__rule">no-metric</span></div>
</div>
<span style="font-size: 18px; color: var(--color-text-tertiary);"></span>
</header>
<div class="critique__body">
<div class="critique__quote">«Redusere ventetid … vesentlig»</div>
<p>«Vesentlig» kan ikke etterprøves. KR-et trenger en tallverdi (i dager / timer) og et utgangspunkt fra T1.</p>
<div class="critique__suggestion">«Median saksbehandlingstid for byggesak reduseres fra 47 dager (T1 2026) til 30 dager innen 31. august 2026.»</div>
</div>
</article>
<article class="critique" data-severity="medium" data-cid="c3">
<header class="critique__head">
<span class="critique__sev"></span>
<div>
<div class="critique__title">Mangler utgangspunkt</div>
<div class="critique__meta"><span>KR1</span> · <span class="critique__rule">missing-baseline</span></div>
</div>
<span style="font-size: 18px; color: var(--color-text-tertiary);"></span>
</header>
<div class="critique__body">
<div class="critique__quote">«… betydelig sammenlignet med i fjor»</div>
<p>«Sammenlignet med i fjor» er en relativ måling uten basisverdi. T1-tallet for selvbetjenings­andel finnes i Tableau-sett <span style="font-family: var(--font-family-mono); font-size: 12px;">tjeneste-kpi-2026q1</span>.</p>
<div class="critique__suggestion">«Andelen henvendelser fullført i selvbetjenings­løsningen økes fra 41 % (T1 2026) til 60 % innen 31. august 2026.»</div>
</div>
</article>
<article class="critique" data-severity="medium" data-cid="c1">
<header class="critique__head">
<span class="critique__sev"></span>
<div>
<div class="critique__title">Vagt verb i Objective</div>
<div class="critique__meta"><span>O</span> · <span class="critique__rule">vague-verb</span></div>
</div>
<span style="font-size: 18px; color: var(--color-text-tertiary);"></span>
</header>
<div class="critique__body">
<div class="critique__quote">«Forbedre digitale tjenester …»</div>
<p>«Forbedre» kan bety nesten hva som helst. Et godt Objective er kvalitativt og inspirerende, men det skal også gi retning. Hva betyr «bedre» for en innbygger her?</p>
<div class="critique__suggestion">«Innbyggere i Bærum får svar på sine kommunale spørsmål i løpet av samme dag — uten å måtte ringe.»</div>
</div>
</article>
<article class="critique" data-severity="medium" data-cid="c6">
<header class="critique__head">
<span class="critique__sev"></span>
<div>
<div class="critique__title">Mangler tidsfrist</div>
<div class="critique__meta"><span>KR4</span> · <span class="critique__rule">no-deadline</span></div>
</div>
<span style="font-size: 18px; color: var(--color-text-tertiary);"></span>
</header>
<div class="critique__body">
<p>KR-et nevner T2-undersøkelsen, men ikke når den gjennomføres eller når resultatet skal foreligge.</p>
<div class="critique__suggestion">«… målt i T2-undersøkelsen som gjennomføres uke 33-35 og rapporteres innen 15. september 2026.»</div>
</div>
</article>
<article class="critique" data-severity="info">
<header class="critique__head">
<span class="critique__sev"></span>
<div>
<div class="critique__title">Hint: Strekk-mål?</div>
<div class="critique__meta">Hele settet · <span class="critique__rule">stretch-suggestion</span></div>
</div>
<span style="font-size: 18px; color: var(--color-text-tertiary);"></span>
</header>
<div class="critique__body">
<p>Tre av fire KR-er ligger under 1,5× nåværende baseline når du har lagt inn tall. OKR fungerer best når 6070 % oppnåelse oppleves som godt arbeid. Vurder strekk på KR1.</p>
</div>
</article>
</div>
</div>
</div>
</div><!-- /writer -->
<!-- TERMINOLOGY -->
<div class="term-drawer">
<h3 class="h3" style="margin-bottom: var(--space-3);">Bærum-spesifikk OKR-ordliste</h3>
<p style="font-size: var(--font-size-sm); color: var(--color-text-secondary); margin-bottom: var(--space-4);">Plugin-en lærte disse begrepene fra Bærums egen styringspraksis. Andre kommuner forker pluginen og fyller på sine egne.</p>
<dl style="margin: 0;">
<div class="term-row">
<dt>Tertial</dt>
<dd>4-måneders styringsperiode (T1: jan-apr, T2: mai-aug, T3: sep-des). Erstatter «kvartal» i Bærums tekstmaler.</dd>
</div>
<div class="term-row">
<dt>Selvbetjenings­andel</dt>
<dd>KPI definert som henvendelser fullført uten saksbehandler-inngripen, kilde: <span style="font-family: var(--font-family-mono); font-size: 12px;">tjeneste-kpi-2026q1</span>.</dd>
</div>
<div class="term-row">
<dt>Innbygger­tilfredshet</dt>
<dd>5-punkts skala fra årlig undersøkelse. Kommunestyrets mål: ≥ 4,0 i alle avdelinger innen 2027.</dd>
</div>
<div class="term-row">
<dt>Strekk-mål</dt>
<dd>Bærums interne term for ambisiøs verdi (mål 70 %), brukt sammen med «forventet verdi» (mål 90 %).</dd>
</div>
</dl>
</div>
</section><!-- /view writer -->
<!-- ========================================================= -->
<!-- VIEW 2: REWRITE (before/after) -->
<!-- ========================================================= -->
<section class="view" data-view-content="rewrite" style="display: none;">
<h3 class="h3">Side ved side: utkast 0.4 → forslag</h3>
<p style="color: var(--color-text-secondary); font-size: var(--font-size-sm); margin-bottom: var(--space-4);">Plugin-ens forslag bruker baseline-tall den hentet fra Bærums KPI-katalog. Du kan godta hver endring enkeltvis.</p>
<div class="diff" style="background: var(--color-surface);">
<div class="diff__summary">
<div class="diff__summary-item"><span class="diff__summary-count" style="color: var(--color-severity-critical);">5</span><span>fjernet</span></div>
<div class="diff__summary-item"><span class="diff__summary-count" style="color: var(--color-severity-low);">+5</span><span>lagt til</span></div>
<div class="diff__summary-item"><span class="diff__summary-count">9</span><span>endringer</span></div>
</div>
<div class="diff__row">
<div class="diff__cell diff__cell--removed">Forbedre digitale tjenester for innbyggerne i Bærum kommune slik at de opplever bedre service.</div>
<div class="diff__cell diff__cell--added">Innbyggere i Bærum får svar på sine kommunale spørsmål i løpet av samme dag — uten å måtte ringe.</div>
</div>
<div class="diff__row">
<div class="diff__cell diff__cell--removed">KR1: Øke andelen henvendelser løst i selvbetjeningsløsningen betydelig sammenlignet med i fjor.</div>
<div class="diff__cell diff__cell--added">KR1: Andelen henvendelser fullført i selvbetjenings­løsningen økes fra 41 % (T1 2026) til 60 % innen 31. august 2026.</div>
</div>
<div class="diff__row">
<div class="diff__cell diff__cell--removed">KR2: Lansere ny chatbot på kommune.no innen utgangen av tertialet.</div>
<div class="diff__cell diff__cell--added">KR2: Andelen innbyggere som får løst sitt spørsmål i første henvendelse økes fra 38 % (T1 2026) til 55 % innen 31. august 2026.</div>
</div>
<div class="diff__row">
<div class="diff__cell diff__cell--removed">KR3: Redusere ventetid for byggesakshenvendelser vesentlig.</div>
<div class="diff__cell diff__cell--added">KR3: Median saksbehandlingstid for byggesak reduseres fra 47 dager (T1 2026) til 30 dager innen 31. august 2026.</div>
</div>
<div class="diff__row">
<div class="diff__cell diff__cell--removed">KR4: Innbyggertilfredshet på 4,2 av 5 målt i T2-undersøkelsen.</div>
<div class="diff__cell diff__cell--added">KR4: Innbyggertilfredshet på 4,2 av 5 målt i T2-undersøkelsen (uke 33-35), rapportert innen 15. september 2026.</div>
</div>
</div>
<div style="display: flex; gap: var(--space-3); justify-content: flex-end; margin-top: var(--space-4);">
<button class="btn btn--ghost">Avvis alle</button>
<button class="btn btn--secondary">Aksepter én og én</button>
<button class="btn btn--primary">Aksepter alle</button>
</div>
</section>
<!-- ========================================================= -->
<!-- VIEW 3: COHORT (anonymous benchmarking) -->
<!-- ========================================================= -->
<section class="view" data-view-content="cohort" style="display: none;">
<h3 class="h3">Hvordan du ligger an mot resten av Bærum</h3>
<p style="color: var(--color-text-secondary); font-size: var(--font-size-sm); margin-bottom: var(--space-4); max-width: var(--measure);">
Anonymisert sammenligning på tvers av avdelinger som bruker samme plugin. Tall hentes lokalt fra OKR-systemet — ingen tekst, kun aggregerte score.
</p>
<div class="cohort-grid">
<div class="cohort-card">
<div class="cohort-card__head">
<span class="cohort-card__name">Ditt sett</span>
<span class="cohort-card__count">Innbygger­tjenester</span>
</div>
<div class="cohort-card__metric">
<span class="cohort-card__metric-num">62</span>
<span class="cohort-card__metric-suffix">/100</span>
</div>
<div style="font-size: 12px; color: var(--color-text-tertiary);">6 åpne funn · 2 høy alvorlighet</div>
</div>
<div class="cohort-card">
<div class="cohort-card__head">
<span class="cohort-card__name">Avd.-median</span>
<span class="cohort-card__count">14 sett</span>
</div>
<div class="cohort-card__metric">
<span class="cohort-card__metric-num">71</span>
<span class="cohort-card__metric-suffix">/100</span>
</div>
<div style="font-size: 12px; color: var(--color-text-tertiary);">P25: 58 · P75: 84</div>
</div>
<div class="cohort-card">
<div class="cohort-card__head">
<span class="cohort-card__name">Kommune-median</span>
<span class="cohort-card__count">87 sett · alle avd.</span>
</div>
<div class="cohort-card__metric">
<span class="cohort-card__metric-num">68</span>
<span class="cohort-card__metric-suffix">/100</span>
</div>
<div style="font-size: 12px; color: var(--color-text-tertiary);">Beste avd.: Eiendom · 81</div>
</div>
</div>
<div style="margin-top: var(--space-6);">
<h4 class="h4">Hyppigste funn på tvers av Bærum (T2 så langt)</h4>
<div class="distribution">
<div class="distribution__row">
<span class="distribution__label">activity-not-outcome</span>
<div class="distribution__track">
<div class="distribution__band" style="left: 18%; right: 28%;"></div>
<div class="distribution__median" style="left: 41%;"><span class="distribution__median-label">41 % av sett</span></div>
</div>
</div>
<div class="distribution__row">
<span class="distribution__label">no-metric</span>
<div class="distribution__track">
<div class="distribution__band" style="left: 12%; right: 42%;"></div>
<div class="distribution__median" style="left: 33%;"><span class="distribution__median-label">33 %</span></div>
</div>
</div>
<div class="distribution__row">
<span class="distribution__label">missing-baseline</span>
<div class="distribution__track">
<div class="distribution__band" style="left: 22%; right: 22%;"></div>
<div class="distribution__median" style="left: 51%;"><span class="distribution__median-label">51 %</span></div>
</div>
</div>
<div class="distribution__row">
<span class="distribution__label">vague-verb</span>
<div class="distribution__track">
<div class="distribution__band" style="left: 30%; right: 18%;"></div>
<div class="distribution__median" style="left: 60%;"><span class="distribution__median-label">60 %</span></div>
</div>
</div>
<div class="distribution__row">
<span class="distribution__label">no-deadline</span>
<div class="distribution__track">
<div class="distribution__band" style="left: 8%; right: 56%;"></div>
<div class="distribution__median" style="left: 24%;"><span class="distribution__median-label">24 %</span></div>
</div>
</div>
</div>
<p style="font-size: 12px; color: var(--color-text-tertiary); margin-top: var(--space-3); font-family: var(--font-family-mono);">
Bånd = P25P75 på tvers av avd. · linje = median andel sett som har minst ett slikt funn
</p>
</div>
</section>
<!-- ========================================================= -->
<!-- VIEW 4: FINAL -->
<!-- ========================================================= -->
<section class="view" data-view-content="final" style="display: none;">
<div class="final-banner">
<div class="final-banner__icon"></div>
<div>
<div style="font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); margin-bottom: 2px;">Klar for godkjenning · score 91/100</div>
<div style="font-size: var(--font-size-sm); opacity: 0.9;">0 høye funn · 1 informasjonshint · alle KR har baseline, mål og frist</div>
</div>
<button class="btn btn--primary">Send til virksomhetsleder</button>
</div>
<article style="background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); padding: var(--space-8); max-width: 800px;">
<div style="font-size: 11px; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: var(--space-2);">Bærum kommune · Innbyggertjenester · T2 2026 · v1.0</div>
<h2 style="font-family: var(--font-family-serif); font-size: 28px; line-height: 1.3; margin: 0 0 var(--space-6); color: var(--color-text-primary);">
Innbyggere i Bærum får svar på sine kommunale spørsmål i løpet av samme dag — uten å måtte ringe.
</h2>
<h3 class="h4" style="margin-bottom: var(--space-4);">Nøkkelresultater</h3>
<div style="display: flex; flex-direction: column; gap: var(--space-3);">
<div style="padding: var(--space-4); background: var(--color-bg-soft); border-left: 3px solid var(--color-scope-okr); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;">
<div style="font-family: var(--font-family-mono); font-size: 11px; color: var(--color-scope-okr); font-weight: var(--font-weight-semibold); margin-bottom: 4px; letter-spacing: 0.06em;">KR1</div>
<div style="font-family: var(--font-family-serif); font-size: 17px; line-height: 1.5;">Andelen henvendelser fullført i selvbetjenings­løsningen økes fra <strong>41 %</strong> (T1 2026) til <strong>60 %</strong> innen 31. august 2026.</div>
</div>
<div style="padding: var(--space-4); background: var(--color-bg-soft); border-left: 3px solid var(--color-scope-okr); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;">
<div style="font-family: var(--font-family-mono); font-size: 11px; color: var(--color-scope-okr); font-weight: var(--font-weight-semibold); margin-bottom: 4px; letter-spacing: 0.06em;">KR2</div>
<div style="font-family: var(--font-family-serif); font-size: 17px; line-height: 1.5;">Andelen innbyggere som får løst sitt spørsmål i første henvendelse økes fra <strong>38 %</strong> (T1 2026) til <strong>55 %</strong> innen 31. august 2026.</div>
</div>
<div style="padding: var(--space-4); background: var(--color-bg-soft); border-left: 3px solid var(--color-scope-okr); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;">
<div style="font-family: var(--font-family-mono); font-size: 11px; color: var(--color-scope-okr); font-weight: var(--font-weight-semibold); margin-bottom: 4px; letter-spacing: 0.06em;">KR3</div>
<div style="font-family: var(--font-family-serif); font-size: 17px; line-height: 1.5;">Median saksbehandlingstid for byggesak reduseres fra <strong>47 dager</strong> (T1 2026) til <strong>30 dager</strong> innen 31. august 2026.</div>
</div>
<div style="padding: var(--space-4); background: var(--color-bg-soft); border-left: 3px solid var(--color-scope-okr); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;">
<div style="font-family: var(--font-family-mono); font-size: 11px; color: var(--color-scope-okr); font-weight: var(--font-weight-semibold); margin-bottom: 4px; letter-spacing: 0.06em;">KR4</div>
<div style="font-family: var(--font-family-serif); font-size: 17px; line-height: 1.5;">Innbyggertilfredshet på <strong>4,2 av 5</strong> målt i T2-undersøkelsen (uke 3335), rapportert innen 15. september 2026.</div>
</div>
</div>
<div style="margin-top: var(--space-8); padding-top: var(--space-5); border-top: 1px solid var(--color-border-subtle); display: flex; justify-content: space-between; font-size: 12px; color: var(--color-text-tertiary); font-family: var(--font-family-mono);">
<span>Eier: Anne Hovde · Innbygger­tjenester</span>
<span>Generert med okr-writer-baerum v2.3 · 12 reviderte uttkast</span>
</div>
</article>
</section>
</main>
</div>
<script>
// Theme toggle
const themeBtn = document.getElementById('theme-toggle');
const setTheme = (t) => {
document.documentElement.setAttribute('data-theme', t);
themeBtn.textContent = t === 'dark' ? 'Lys' : 'Mørk';
themeBtn.setAttribute('aria-pressed', t === 'dark' ? 'true' : 'false');
try { localStorage.setItem('pg-theme', t); } catch(e) {}
};
setTheme(localStorage.getItem('pg-theme') || 'light');
themeBtn.addEventListener('click', () => {
setTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');
});
// View toggle
const views = document.querySelectorAll('[data-view-content]');
document.querySelectorAll('.view-toggle button').forEach(btn => {
btn.addEventListener('click', () => {
const v = btn.dataset.view;
document.querySelectorAll('.view-toggle button').forEach(b => b.setAttribute('aria-pressed', b === btn ? 'true' : 'false'));
views.forEach(s => { s.style.display = s.dataset.viewContent === v ? '' : 'none'; });
try { history.replaceState(null, '', '#' + v); } catch(e) {}
});
});
// initial from hash
const initialView = (location.hash || '').replace('#','') || 'writer';
const tab = document.querySelector(`[data-view="${initialView}"]`);
if (tab) tab.click();
// Critique <-> editor highlighting
const editor = document.getElementById('editor');
document.querySelectorAll('.critique').forEach(c => {
c.querySelector('.critique__head').addEventListener('click', () => {
document.querySelectorAll('.critique').forEach(x => x.removeAttribute('data-active'));
c.setAttribute('data-active', 'true');
const cid = c.dataset.cid;
if (cid) {
const target = editor.querySelector(`[data-cid="${cid}"]`);
if (target) {
target.style.transition = 'background-color 0.6s';
target.style.backgroundColor = 'rgba(0, 98, 186, 0.18)';
setTimeout(() => { target.style.backgroundColor = ''; }, 1400);
}
}
});
});
// Hover linking from editor to critique
editor.querySelectorAll('.hl').forEach(hl => {
hl.addEventListener('mouseenter', () => {
const cid = hl.dataset.cid;
const c = document.querySelector(`.critique[data-cid="${cid}"]`);
if (c) c.style.outline = '2px solid var(--color-primary-300)';
});
hl.addEventListener('mouseleave', () => {
const cid = hl.dataset.cid;
const c = document.querySelector(`.critique[data-cid="${cid}"]`);
if (c) c.style.outline = '';
});
});
</script>
</body>
</html>