ktg-plugin-marketplace/shared/playground-examples/okr-baerum.html
Kjell Tore Guttormsen f1fecf39b8 feat(shared): Tier 3 wave 2 (12 components) + self-hosted fonts
Two changes in one commit because they were prepared together and the
component demos depend on the new self-hosted fonts.css.

Tier 3 wave 2 — 12 new components
---------------------------------
Adds components-tier3-supplement.css (886 lines) and 12 isolated demo
HTML pages under shared/playground-examples/components/:
toxic-flow chain, fleet-overview, kanban Keep/Review/Remove,
maturity-ladder, classify-and-transform, cycle-ribbon,
persistent-antipattern, suppressed-signals, ExpansionCard, ReadMore,
FormProgress, Aspirational-vs-Committed.

Reuses existing tokens — no new CSS custom properties. Honors the
Phase 1 feedback rules: no large pink areas for body text, severity-red
distinct from failure-red, dark mode via existing [data-theme="dark"].

Provenance: components-tier3-supplement.css and the 12 demo bodies were
authored by claude.ai/design (separate Anthropic instance) on 2026-05-03.
This commit only integrates them — path rewrites, font swap, generic
name substitution in fleet-overview demo data, README updates.
base.css from the export was deliberately NOT taken in because it
reverted the inline-message contrast fix from v0.1.

Self-hosted fonts (Inter, JetBrains Mono, Source Serif 4)
---------------------------------------------------------
Replaces all fonts.googleapis.com / fonts.gstatic.com requests with
.woff2 files bundled at shared/playground-design-system/fonts/.

Why:
- No data leaked to Google about end-user IPs and User-Agents.
- GDPR-safe for Norwegian public-sector deployments.
- Works offline / behind air-gapped firewalls.
- Forkers downloading the marketplace get a complete bundle.

All three families are SIL Open Font License 1.1 — license texts
included alongside the woff2 files. Source Serif 4 woff2 generated
locally from the upstream OTF release using
fonttools ttLib.woff2 compress; Inter and JetBrains Mono are
unmodified upstream webfont releases.

Total bundle: 9 woff2 files, ~940 KB. New fonts.css declares all
@font-face rules with font-display: swap. All 6 example HTMLs and 12
new component demos load it via a single relative path.

Verified
--------
- Privacy grep returns empty across plugins/ and shared/
- Google Fonts grep returns empty across shared/*.html
- Smoke test via python -m http.server: HTML + 7 stylesheets +
  Inter-Regular.woff2 all return 200

Doc updates
-----------
- shared/playground-design-system/README.md: file tree updated,
  Quick start snippet shows fonts.css link, "Self-hosted fonts"
  section added
- shared/playground-design-system/fonts/LICENSES.md: combined attribution
- README.md (root): Tier 3 wave 1+2 component list, Privacy-first bullet
- CLAUDE.md (root): tree entry expanded for new components + fonts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 05:08:07 +02:00

866 lines
44 KiB
HTML
Raw Permalink 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="stylesheet" href="../playground-design-system/fonts.css" />
<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>