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>
516 lines
31 KiB
HTML
516 lines
31 KiB
HTML
<!doctype html>
|
||
<html lang="nb">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>ROS — M365 Copilot — Lier kommune</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/fonts.css" />
|
||
<style>
|
||
/* Page-specific layout */
|
||
.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-architect); 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; }
|
||
|
||
.verdict {
|
||
display: inline-flex; align-items: center; gap: 8px;
|
||
padding: 6px 14px;
|
||
border-radius: var(--radius-pill);
|
||
font-weight: var(--font-weight-semibold);
|
||
font-size: var(--font-size-sm);
|
||
}
|
||
.verdict[data-verdict="go-with-conditions"] { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
|
||
.verdict[data-verdict="block"] { background: var(--color-severity-critical); color: #fff; }
|
||
.verdict[data-verdict="approved"] { background: var(--color-severity-low-soft); color: var(--color-severity-low-on); }
|
||
.verdict__dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
|
||
|
||
.screen-tabs {
|
||
display: flex; gap: var(--space-1); padding: 4px;
|
||
background: var(--color-bg-soft); border-radius: var(--radius-md);
|
||
width: fit-content;
|
||
}
|
||
.screen-tab {
|
||
padding: 8px 14px; font-size: var(--font-size-sm); 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;
|
||
}
|
||
.screen-tab[aria-current="true"] { background: var(--color-surface); color: var(--color-text-primary); box-shadow: var(--shadow-sm); }
|
||
|
||
.screen { display: none; }
|
||
.screen[data-active="true"] { display: block; }
|
||
|
||
/* Two-col with sidebar */
|
||
.ros-layout { display: grid; grid-template-columns: 1fr 320px; gap: var(--space-8); align-items: start; }
|
||
@media (max-width: 980px) { .ros-layout { grid-template-columns: 1fr; } }
|
||
|
||
.key-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); margin-bottom: var(--space-6); }
|
||
.key-stat { padding: var(--space-4); background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); }
|
||
.key-stat__label { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); margin-bottom: 4px; }
|
||
.key-stat__value { font-size: var(--font-size-2xl); font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; line-height: 1.1; }
|
||
.key-stat__hint { font-size: var(--font-size-xs); color: var(--color-text-secondary); margin-top: 2px; }
|
||
.key-stat--critical .key-stat__value { color: var(--color-severity-critical); }
|
||
.key-stat--medium .key-stat__value { color: var(--color-severity-medium-on); }
|
||
@media (max-width: 720px) { .key-stats { grid-template-columns: repeat(2, 1fr); } }
|
||
|
||
/* Top risks list */
|
||
.top-risks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
|
||
.top-risk { display: grid; grid-template-columns: auto 36px 1fr auto; gap: var(--space-3); align-items: center; padding: 10px 12px; border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); background: var(--color-surface); cursor: pointer; }
|
||
.top-risk:hover { border-color: var(--color-border-moderate); }
|
||
.top-risk__rank { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-text-tertiary); width: 24px; }
|
||
.top-risk__score { font-weight: var(--font-weight-semibold); font-variant-numeric: tabular-nums; padding: 4px 8px; border-radius: var(--radius-sm); text-align: center; min-width: 36px; font-size: var(--font-size-sm); }
|
||
.top-risk__score[data-zone="critical"] { background: var(--color-severity-critical); color: #fff; }
|
||
.top-risk__score[data-zone="high"] { background: var(--color-severity-high); color: #fff; }
|
||
.top-risk__score[data-zone="medium"] { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
|
||
.top-risk__score[data-zone="low"] { background: var(--color-severity-low-soft); color: var(--color-severity-low-on); }
|
||
.top-risk__title { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); }
|
||
.top-risk__id { font-family: var(--font-family-mono); font-size: 11px; color: var(--color-text-tertiary); margin-bottom: 2px; }
|
||
.top-risk__delta { font-size: var(--font-size-xs); color: var(--color-text-tertiary); font-variant-numeric: tabular-nums; }
|
||
|
||
/* Detail panel */
|
||
.threat-detail { display: flex; flex-direction: column; gap: var(--space-5); }
|
||
.threat-detail__title { font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); line-height: 1.3; }
|
||
.threat-detail__id { font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
|
||
.threat-detail__scores {
|
||
display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3);
|
||
padding: var(--space-4); background: var(--color-bg-soft); border-radius: var(--radius-md);
|
||
}
|
||
.threat-detail__score-block { display: flex; flex-direction: column; gap: 2px; }
|
||
.threat-detail__score-label { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-tertiary); }
|
||
.threat-detail__score-value { font-size: var(--font-size-xl); font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; }
|
||
.threat-detail__section h4 { font-size: var(--font-size-sm); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-secondary); margin-bottom: 6px; font-weight: var(--font-weight-semibold); }
|
||
.threat-detail__section p { font-size: var(--font-size-sm); line-height: var(--line-height-normal); }
|
||
|
||
.residual-pair { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--space-3); align-items: center; padding: var(--space-3); background: var(--color-surface); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); }
|
||
.residual-cell { text-align: center; padding: var(--space-3); border-radius: var(--radius-sm); }
|
||
.residual-cell__label { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.06em; }
|
||
.residual-cell__value { font-size: var(--font-size-2xl); font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; line-height: 1.1; margin-top: 2px; }
|
||
.residual-cell[data-zone="critical"] { background: var(--color-severity-critical-soft); color: var(--color-severity-critical-on); }
|
||
.residual-cell[data-zone="critical"] .residual-cell__value { color: var(--color-severity-critical); }
|
||
.residual-cell[data-zone="high"] { background: var(--color-severity-high-soft); color: var(--color-severity-high-on); }
|
||
.residual-cell[data-zone="medium"] { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
|
||
.residual-cell[data-zone="low"] { background: var(--color-severity-low-soft); color: var(--color-severity-low-on); }
|
||
.residual-arrow { color: var(--color-text-tertiary); font-size: 22px; }
|
||
|
||
.mitigation-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--space-2); }
|
||
.mitigation { display: grid; grid-template-columns: auto 1fr auto; gap: var(--space-3); padding: 10px 12px; border: 1px solid var(--color-border-subtle); border-radius: var(--radius-sm); align-items: center; font-size: var(--font-size-sm); }
|
||
.mitigation__id { font-family: var(--font-family-mono); font-size: 11px; color: var(--color-text-tertiary); }
|
||
.mitigation__status { font-size: var(--font-size-xs); padding: 2px 8px; border-radius: var(--radius-pill); font-weight: var(--font-weight-medium); }
|
||
.mitigation__status[data-status="implemented"] { background: var(--color-severity-low-soft); color: var(--color-severity-low-on); }
|
||
.mitigation__status[data-status="planned"] { background: var(--color-severity-medium-soft); color: var(--color-severity-medium-on); }
|
||
.mitigation__status[data-status="proposed"] { background: var(--color-bg-soft); color: var(--color-text-secondary); }
|
||
|
||
/* Wizard form */
|
||
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5) var(--space-6); }
|
||
.form-grid > .form-grid__full { grid-column: 1 / -1; }
|
||
@media (max-width: 720px) { .form-grid { grid-template-columns: 1fr; } }
|
||
|
||
.chip-group { display: flex; flex-wrap: wrap; gap: 6px; }
|
||
.chip-input { display: none; }
|
||
.chip-input + label {
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
padding: 7px 12px; font-size: var(--font-size-sm);
|
||
border: 1px solid var(--color-border-moderate);
|
||
border-radius: var(--radius-pill);
|
||
background: var(--color-surface);
|
||
color: var(--color-text-secondary);
|
||
cursor: pointer;
|
||
user-select: none;
|
||
}
|
||
.chip-input + label:hover { border-color: var(--color-border-strong); color: var(--color-text-primary); }
|
||
.chip-input:checked + label {
|
||
background: var(--color-primary-500); color: #fff; border-color: var(--color-primary-500);
|
||
}
|
||
.chip-input:focus-visible + label { box-shadow: var(--shadow-focus); }
|
||
|
||
/* Summary screen */
|
||
.summary-grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: var(--space-6); }
|
||
@media (max-width: 980px) { .summary-grid { grid-template-columns: 1fr; } }
|
||
|
||
.recommendation-card {
|
||
padding: var(--space-6); border: 1px solid var(--color-border-subtle); border-radius: var(--radius-lg);
|
||
background: var(--color-surface);
|
||
}
|
||
.recommendation-card__verdict-line { display: flex; gap: var(--space-3); align-items: center; margin-bottom: var(--space-4); }
|
||
.recommendation-card__verdict-line h2 { margin: 0; }
|
||
.recommendation-card__conditions { list-style: none; padding: 0; margin: var(--space-3) 0 0; }
|
||
.recommendation-card__conditions li {
|
||
padding: 8px 0 8px 28px; position: relative; font-size: var(--font-size-sm); line-height: var(--line-height-snug);
|
||
border-top: 1px solid var(--color-border-subtle);
|
||
}
|
||
.recommendation-card__conditions li:first-child { border-top: none; }
|
||
.recommendation-card__conditions li::before {
|
||
content: ''; position: absolute; left: 0; top: 14px;
|
||
width: 16px; height: 16px; border-radius: 50%;
|
||
border: 1.5px solid var(--color-border-moderate);
|
||
}
|
||
|
||
/* Print rules */
|
||
@media print {
|
||
.app-header, .screen-tabs, .wizard__nav, .no-print { display: none !important; }
|
||
.screen { display: block !important; page-break-after: always; }
|
||
.ros-layout { grid-template-columns: 1fr; }
|
||
.matrix__cell { print-color-adjust: exact; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="layout">
|
||
<!-- ============== HEADER ============== -->
|
||
<header class="app-header no-print">
|
||
<a href="index.html" class="app-header__brand">
|
||
<span class="app-header__brand-mark">A</span>
|
||
<span>ms-ai-architect</span>
|
||
</a>
|
||
<span class="app-header__breadcrumb">
|
||
<span aria-hidden="true">/</span>
|
||
<span>Playground</span>
|
||
<span aria-hidden="true">/</span>
|
||
<span>ROS-analyse</span>
|
||
</span>
|
||
<span class="app-header__spacer"></span>
|
||
<span class="badge badge--scope-architect">ms-ai-architect</span>
|
||
<button type="button" class="theme-toggle" id="themeToggle" aria-label="Bytt tema">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
||
<span id="themeLabel">Mørkt</span>
|
||
</button>
|
||
<button type="button" class="btn btn--secondary btn--sm" onclick="window.print()">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M6 9V2h12v7M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2M6 14h12v8H6z"/></svg>
|
||
Skriv ut
|
||
</button>
|
||
</header>
|
||
|
||
<main class="page">
|
||
<div class="container container--wide">
|
||
<!-- Page header -->
|
||
<header class="page__header">
|
||
<div class="page__title">
|
||
<span class="page__eyebrow">ROS — Risiko- og Sårbarhetsanalyse · NS 5814</span>
|
||
<h1>M365 Copilot Enterprise — Lier kommune</h1>
|
||
<div class="page__meta" style="margin-top: 8px;">
|
||
<span class="page__meta-item"><span class="page__meta-label">ID</span> <code>ROS-2026-LIER-COPILOT-01</code></span>
|
||
<span class="page__meta-item"><span class="page__meta-label">Brukerantall</span> <span class="tabular">1 850</span></span>
|
||
<span class="page__meta-item"><span class="page__meta-label">Sektor</span> Kommune (~28 000)</span>
|
||
<span class="page__meta-item"><span class="page__meta-label">Sist oppdatert</span> 1. mai 2026</span>
|
||
</div>
|
||
</div>
|
||
<div class="row" style="gap: var(--space-3);">
|
||
<span class="verdict" data-verdict="go-with-conditions">
|
||
<span class="verdict__dot" aria-hidden="true"></span>
|
||
GO med betingelser
|
||
</span>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Tabs -->
|
||
<nav class="screen-tabs no-print" role="tablist" aria-label="ROS-skjermer">
|
||
<button type="button" class="screen-tab" role="tab" aria-current="true" data-screen="intake">1 · Intake</button>
|
||
<button type="button" class="screen-tab" role="tab" aria-current="false" data-screen="matrix">2 · Risikomatrise</button>
|
||
<button type="button" class="screen-tab" role="tab" aria-current="false" data-screen="findings">3 · Funn</button>
|
||
<button type="button" class="screen-tab" role="tab" aria-current="false" data-screen="summary">4 · Sammendrag</button>
|
||
</nav>
|
||
|
||
<!-- ========================================================
|
||
SCREEN 1 — INTAKE WIZARD
|
||
======================================================== -->
|
||
<section class="screen" data-screen="intake" data-active="false" style="margin-top: var(--space-6);">
|
||
<nav class="stepper" aria-label="Intake-steg">
|
||
<button type="button" class="stepper__step" data-state="active">
|
||
<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">Kommune, sektor, størrelse</span>
|
||
</span>
|
||
</button>
|
||
<button type="button" class="stepper__step" data-state="pending">
|
||
<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">Lisens, residens, brukere</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">Datasensitivitet</span>
|
||
<span class="stepper__step-hint">Persondata-kategorier</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">Compliance</span>
|
||
<span class="stepper__step-hint">Rammeverk og krav</span>
|
||
</span>
|
||
</button>
|
||
<button type="button" class="stepper__step" data-state="pending">
|
||
<span class="stepper__step-number"><span class="stepper__step-number-text">5</span></span>
|
||
<span class="stepper__step-text">
|
||
<span class="stepper__step-label">Bekreft</span>
|
||
<span class="stepper__step-hint">Generer ROS</span>
|
||
</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="stack stack--lg" style="max-width: 880px;">
|
||
<h2>Organisasjonsprofil</h2>
|
||
<p class="text-secondary" style="font-size: var(--font-size-md);">
|
||
Vi tilpasser ROS-malen til virksomheten din. Felter merket med skarpere ramme er obligatoriske for å sende inn til Datatilsynet.
|
||
</p>
|
||
|
||
<div class="form-grid">
|
||
<div>
|
||
<label class="label" for="orgName">Virksomhet
|
||
<span class="label__hint">Etat, kommune eller foretak</span>
|
||
</label>
|
||
<input type="text" class="input" id="orgName" value="Lier kommune" />
|
||
</div>
|
||
<div>
|
||
<label class="label" for="orgSize">Antall ansatte<span class="label__hint">Påvirker brukerbase i scenarioer</span></label>
|
||
<input type="number" class="input" id="orgSize" value="1850" />
|
||
</div>
|
||
<div class="form-grid__full">
|
||
<span class="label">Sektor</span>
|
||
<div class="chip-group">
|
||
<input type="radio" name="sector" id="s-kommune" class="chip-input" checked />
|
||
<label for="s-kommune">Kommune</label>
|
||
<input type="radio" name="sector" id="s-fylke" class="chip-input" />
|
||
<label for="s-fylke">Fylkeskommune</label>
|
||
<input type="radio" name="sector" id="s-etat" class="chip-input" />
|
||
<label for="s-etat">Statlig etat</label>
|
||
<input type="radio" name="sector" id="s-helse" class="chip-input" />
|
||
<label for="s-helse">Helseforetak</label>
|
||
<input type="radio" name="sector" id="s-foretak" class="chip-input" />
|
||
<label for="s-foretak">Statlig foretak</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-grid__full">
|
||
<span class="label">Eksisterende lisenser<span class="label__hint">Brukes til å vurdere kapabilitetsmatrise</span></span>
|
||
<div class="chip-group">
|
||
<input type="checkbox" id="l-e3" class="chip-input" />
|
||
<label for="l-e3">M365 E3</label>
|
||
<input type="checkbox" id="l-e5" class="chip-input" checked />
|
||
<label for="l-e5">M365 E5</label>
|
||
<input type="checkbox" id="l-purview" class="chip-input" checked />
|
||
<label for="l-purview">Purview</label>
|
||
<input type="checkbox" id="l-defender" class="chip-input" checked />
|
||
<label for="l-defender">Defender for Cloud Apps</label>
|
||
<input type="checkbox" id="l-sov" class="chip-input" />
|
||
<label for="l-sov">Cloud for Sovereignty</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-grid__full">
|
||
<div class="inline-message inline-message--info">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true" style="flex-shrink:0;"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
|
||
<span>Lier har ikke aktivert <strong>Microsoft Cloud for Sovereignty</strong>. Vi vurderer Schrems II-eksponering som forhøyet inntil dette er på plass.</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wizard__nav">
|
||
<button type="button" class="btn btn--secondary" disabled>Forrige</button>
|
||
<div class="row">
|
||
<button type="button" class="btn btn--ghost">Lagre utkast</button>
|
||
<button type="button" class="btn btn--primary" data-goto="matrix">Neste: System →</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ========================================================
|
||
SCREEN 2 — RISK MATRIX (centerpiece)
|
||
======================================================== -->
|
||
<section class="screen" data-screen="matrix" data-active="true" style="margin-top: var(--space-6);">
|
||
<div class="key-stats">
|
||
<div class="key-stat">
|
||
<div class="key-stat__label">Identifiserte trusler</div>
|
||
<div class="key-stat__value tabular">49</div>
|
||
<div class="key-stat__hint">Av 64 i kanonisk katalog</div>
|
||
</div>
|
||
<div class="key-stat key-stat--critical">
|
||
<div class="key-stat__label">Kritiske (rød sone)</div>
|
||
<div class="key-stat__value tabular">7</div>
|
||
<div class="key-stat__hint">Score 15–25 før tiltak</div>
|
||
</div>
|
||
<div class="key-stat key-stat--medium">
|
||
<div class="key-stat__label">Mitigeringer planlagt</div>
|
||
<div class="key-stat__value tabular">31</div>
|
||
<div class="key-stat__hint">Reduserer 22 trusler</div>
|
||
</div>
|
||
<div class="key-stat">
|
||
<div class="key-stat__label">Restrisiko etter tiltak</div>
|
||
<div class="key-stat__value tabular">2</div>
|
||
<div class="key-stat__hint">Krever GO-betingelser</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ros-layout">
|
||
<!-- Matrix -->
|
||
<div class="card" style="padding: var(--space-6);">
|
||
<div style="display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--space-4);">
|
||
<div>
|
||
<h2>5×5 Risikomatrise</h2>
|
||
<p class="text-secondary text-sm" style="margin-top: 4px;">49 trusler plassert etter sannsynlighet × konsekvens. Klikk en celle for å se trusler.</p>
|
||
</div>
|
||
<div class="row" style="gap: var(--space-2);">
|
||
<button type="button" class="btn btn--ghost btn--sm" id="toggleResidual">Vis restrisiko etter tiltak</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="matrix">
|
||
<div class="matrix__y-label">Konsekvens</div>
|
||
<div class="matrix__main">
|
||
<div class="matrix__grid" id="rosMatrix">
|
||
<!-- populated by JS -->
|
||
</div>
|
||
<div class="matrix__x-label">Sannsynlighet →</div>
|
||
<div class="matrix__legend">
|
||
<span><span class="matrix__legend-swatch" style="background: var(--color-severity-low-soft)"></span>Lav (1–8)</span>
|
||
<span><span class="matrix__legend-swatch" style="background: var(--color-severity-medium-soft)"></span>Middels (9–12)</span>
|
||
<span><span class="matrix__legend-swatch" style="background: var(--color-severity-high-soft)"></span>Høy (15–16)</span>
|
||
<span><span class="matrix__legend-swatch" style="background: var(--color-severity-critical-soft)"></span>Kritisk (20–25)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Sidebar: 7-axis radar -->
|
||
<aside class="card">
|
||
<h3 style="margin-bottom: var(--space-4);">Dimensjons-radar</h3>
|
||
<p class="text-secondary text-sm" style="margin-bottom: var(--space-4);">7 NS 5814-akser, vektet etter dataresidens og brukerantall.</p>
|
||
<div class="radar">
|
||
<div class="radar__chart">
|
||
<svg viewBox="-130 -130 260 260" class="radar__svg" aria-label="Dimensjons-radar">
|
||
<g id="radarGrid"></g>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
<div class="radar__legend" style="margin-top: var(--space-3);">
|
||
<div class="radar__legend-item"><span class="radar__legend-swatch radar__legend-swatch--current"></span><span>Nåværende risiko</span></div>
|
||
<div class="radar__legend-item"><span class="radar__legend-swatch radar__legend-swatch--target"></span><span>Etter mitigeringer</span></div>
|
||
</div>
|
||
<dl class="radar__scores" id="radarScores"></dl>
|
||
</aside>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ========================================================
|
||
SCREEN 3 — FINDINGS BROWSER
|
||
======================================================== -->
|
||
<section class="screen" data-screen="findings" data-active="false" style="margin-top: var(--space-6);">
|
||
<div class="findings">
|
||
<div class="findings__list" role="region" aria-label="Trusselliste">
|
||
<div class="findings__toolbar">
|
||
<input type="search" class="findings__search" placeholder="Søk trusler…" aria-label="Søk" />
|
||
<button type="button" class="btn btn--ghost btn--sm" aria-label="Filter" title="Filter">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/></svg>
|
||
</button>
|
||
</div>
|
||
<div id="findingsGroups" style="overflow-y: auto;"></div>
|
||
</div>
|
||
|
||
<div class="findings__detail" id="findingDetail">
|
||
<!-- Populated -->
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ========================================================
|
||
SCREEN 4 — SUMMARY (print-ready)
|
||
======================================================== -->
|
||
<section class="screen" data-screen="summary" data-active="false" style="margin-top: var(--space-6);">
|
||
<div class="summary-grid">
|
||
<!-- Top risks -->
|
||
<div class="card">
|
||
<h2>Topp 5 risikoer</h2>
|
||
<p class="text-secondary text-sm" style="margin-top: 4px; margin-bottom: var(--space-4);">Sortert etter score før tiltak. Pil viser endring etter mitigering.</p>
|
||
<ol class="top-risks" id="topRisks"></ol>
|
||
</div>
|
||
|
||
<!-- Recommendation -->
|
||
<div class="recommendation-card">
|
||
<div class="recommendation-card__verdict-line">
|
||
<span class="verdict" data-verdict="go-with-conditions">
|
||
<span class="verdict__dot" aria-hidden="true"></span>
|
||
GO med betingelser
|
||
</span>
|
||
</div>
|
||
<h2>Anbefaling</h2>
|
||
<p style="margin-top: var(--space-3); font-size: var(--font-size-md); line-height: var(--line-height-normal);">
|
||
Utrullingen kan gå videre forutsatt at fire kontroller er på plass før første pilotgruppe får tilgang. To av de syv kritiske truslene har restrisiko som krever oppfølging på tertialvis nivå.
|
||
</p>
|
||
<h4 style="margin-top: var(--space-5); font-size: var(--font-size-sm); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-secondary);">Betingelser</h4>
|
||
<ol class="recommendation-card__conditions">
|
||
<li><strong>Sensitivity Labels</strong> aktivert på alle SharePoint-områder med personopplysninger (M-001).</li>
|
||
<li><strong>EU Data Boundary</strong> bekreftet før første prompt (M-003).</li>
|
||
<li><strong>Endpoint DLP</strong> rullet ut til alle 1 850 ansatte (M-002).</li>
|
||
<li><strong>Tertialvis evaluering</strong> av T-007 og T-019 i sikkerhetsforum.</li>
|
||
</ol>
|
||
<div style="margin-top: var(--space-6); display: flex; gap: var(--space-2); flex-wrap: wrap;">
|
||
<button type="button" class="btn btn--primary">Eksporter PDF</button>
|
||
<button type="button" class="btn btn--secondary">Kopier slash-pipeline</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Compliance -->
|
||
<div class="card" style="grid-column: 1 / -1;">
|
||
<h2>Rammeverk-dekning</h2>
|
||
<p class="text-secondary text-sm" style="margin-top: 4px; margin-bottom: var(--space-4);">Hvilke krav ROS-en hjemler. Klikk for detaljer.</p>
|
||
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-3);">
|
||
<div class="card card--sunken" style="padding: var(--space-3);">
|
||
<div class="text-xs text-secondary" style="text-transform: uppercase; letter-spacing: 0.06em;">NS 5814:2021</div>
|
||
<div style="font-weight: 600; font-size: var(--font-size-sm); margin-top: 4px;">Dekket — 7/7 dimensjoner</div>
|
||
</div>
|
||
<div class="card card--sunken" style="padding: var(--space-3);">
|
||
<div class="text-xs text-secondary" style="text-transform: uppercase; letter-spacing: 0.06em;">GDPR Art. 35</div>
|
||
<div style="font-weight: 600; font-size: var(--font-size-sm); margin-top: 4px;">Krever DPIA — utløst</div>
|
||
</div>
|
||
<div class="card card--sunken" style="padding: var(--space-3);">
|
||
<div class="text-xs text-secondary" style="text-transform: uppercase; letter-spacing: 0.06em;">EU AI Act</div>
|
||
<div style="font-weight: 600; font-size: var(--font-size-sm); margin-top: 4px;">Begrenset risiko (Art. 50)</div>
|
||
</div>
|
||
<div class="card card--sunken" style="padding: var(--space-3);">
|
||
<div class="text-xs text-secondary" style="text-transform: uppercase; letter-spacing: 0.06em;">Digitaliseringsdir.</div>
|
||
<div style="font-weight: 600; font-size: var(--font-size-sm); margin-top: 4px;">Veileder fulgt</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<!-- ============== SIDEPANEL ============== -->
|
||
<div class="scrim" id="scrim" aria-hidden="true"></div>
|
||
<aside class="sidepanel" id="sidepanel" role="dialog" aria-modal="true" aria-labelledby="sidepanelTitle" aria-hidden="true">
|
||
<div class="sidepanel__header">
|
||
<div>
|
||
<div class="text-xs text-tertiary text-mono" id="sidepanelId" style="margin-bottom: 4px;"></div>
|
||
<h2 id="sidepanelTitle" style="font-size: var(--font-size-lg);"></h2>
|
||
</div>
|
||
<button type="button" class="sidepanel__close" id="sidepanelClose" aria-label="Lukk">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18M6 6l12 12"/></svg>
|
||
</button>
|
||
</div>
|
||
<div class="sidepanel__body" id="sidepanelBody"></div>
|
||
</aside>
|
||
|
||
<script src="ros-data.js"></script>
|
||
<script src="ros-app.js"></script>
|
||
</body>
|
||
</html>
|