docs(2): kort visuell presentasjon — 12 slides, norsk, verdiskaping — som følger repoet

Ny fil (aldri en beskjæring av fagdecket): selvbærende HTML, håndskrevne
tema-bevisste SVG-er, meta charset, null eksterne avhengigheter. Hvert tall
produsert av en kommando mot fasiten tests/golden/demo-transcript.stdout.
Slide 12 er ærlig status: aldri kjørt mot levende modell ER innholdet.
README lenker decket med én engelsk linje.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FmTp2wg9GxRuENkHECpdE
This commit is contained in:
Kjell Tore Guttormsen 2026-08-14 08:10:42 +02:00
commit 4da00cefb0
2 changed files with 545 additions and 0 deletions

View file

@ -20,6 +20,9 @@ decides the numbers; domain experts judge the outcomes (human-in-the-loop); and
*AI-generated: all code produced by Claude Code through dialog-driven development, with human
review, test and judgement before anything ships.*
A short visual introduction — 12 slides, in Norwegian, for a general audience — ships with the
repo: open [docs/kort-presentasjon.html](docs/kort-presentasjon.html) in any browser.
## Install
Python ≥3.10, with [`uv`](https://docs.astral.sh/uv/). The package is not published to a package

542
docs/kort-presentasjon.html Normal file
View file

@ -0,0 +1,542 @@
<!doctype html>
<meta charset="utf-8">
<title>Pengene inne i porteføljen</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
--ground: #F6F5F1; --surface: #FFFFFF; --surface-2: #EFEDE6;
--ink: #22272B; --muted: #5C6570; --line: #D9D6CC;
--accent: #C89B00; --accent-ink: #7A5F00; --steel: #35566F;
--ok-bg: #E3F0E7; --ok-fg: #1F5C38;
--warn-bg: #F6ECD4; --warn-fg: #7A5410;
--bad-bg: #F5E0DD; --bad-fg: #8C3128;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--ground: #15181B; --surface: #1D2126; --surface-2: #23282E;
--ink: #E9E7E1; --muted: #9AA3AC; --line: #343A41;
--accent: #E3B93F; --accent-ink: #E3B93F; --steel: #8FB4D2;
--ok-bg: #1E3327; --ok-fg: #8FCCA6;
--warn-bg: #38301A; --warn-fg: #E0BE6A;
--bad-bg: #3A2523; --bad-fg: #E09A92;
}
}
:root[data-theme="dark"] {
--ground: #15181B; --surface: #1D2126; --surface-2: #23282E;
--ink: #E9E7E1; --muted: #9AA3AC; --line: #343A41;
--accent: #E3B93F; --accent-ink: #E3B93F; --steel: #8FB4D2;
--ok-bg: #1E3327; --ok-fg: #8FCCA6;
--warn-bg: #38301A; --warn-fg: #E0BE6A;
--bad-bg: #3A2523; --bad-fg: #E09A92;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
background: var(--ground); color: var(--ink);
font-family: Charter, "Bitstream Charter", Cambria, Georgia, serif;
margin: 0; overflow: hidden;
}
h1, h2, .kicker, .btn, .counter, .pill, .cmd, ul.plist {
font-family: -apple-system, "Segoe UI", system-ui, "Helvetica Neue", Arial, sans-serif;
}
.slide {
display: none; position: absolute; inset: 0;
padding: clamp(30px, 4.4vw, 72px) clamp(28px, 7vw, 116px) clamp(56px, 7vh, 88px);
flex-direction: column; justify-content: center;
}
.slide.active { display: flex; }
.inner { width: 100%; max-width: 1060px; margin: 0 auto; }
.kicker {
font-size: clamp(0.68rem, 1vw, 0.8rem); font-weight: 650;
text-transform: uppercase; letter-spacing: 0.12em;
color: var(--accent-ink); margin-bottom: 16px;
}
h1 { font-size: clamp(2rem, 4.8vw, 3.5rem); font-weight: 650; letter-spacing: -0.02em; line-height: 1.09; margin: 0 0 22px; text-wrap: balance; }
h2 { font-size: clamp(1.4rem, 3vw, 2.25rem); font-weight: 650; letter-spacing: -0.015em; line-height: 1.18; margin: 0 0 18px; text-wrap: balance; max-width: 26ch; }
/* selvbaerende brodtekst - hver slide skal kunne leses alene */
.say {
font-size: clamp(1rem, 1.55vw, 1.24rem);
line-height: 1.6; max-width: 62ch; margin: 0 0 1rem;
}
.say.dim { color: var(--muted); }
.say strong { font-weight: 650; }
.say:last-child { margin-bottom: 0; }
.lead-sub { font-size: clamp(1.05rem, 1.8vw, 1.4rem); color: var(--muted); max-width: 46ch; line-height: 1.5; }
code { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 0.88em;
background: var(--surface-2); border-radius: 3px; padding: 1px 5px; }
.cmd {
background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--accent);
padding: 20px 26px; margin: 6px 0 20px;
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: clamp(0.82rem, 1.35vw, 1.02rem); line-height: 2.0; overflow-x: auto; white-space: pre;
}
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-top: 6px; }
.card { background: var(--surface); border: 1px solid var(--line); border-top: 3px solid var(--accent); padding: 20px 22px; }
.card .t { font-family: -apple-system, system-ui, sans-serif; font-weight: 650; font-size: clamp(0.98rem, 1.4vw, 1.1rem); display: block; margin-bottom: 12px; }
.card.good { border-top-color: var(--ok-fg); }
.card.good .t { color: var(--ok-fg); }
.card.risk { border-top-color: var(--bad-fg); }
.card.risk .t { color: var(--bad-fg); }
ul.plist { list-style: none; padding: 0; margin: 0; display: grid; gap: 11px; }
ul.plist li { font-size: clamp(0.86rem, 1.22vw, 1rem); line-height: 1.5; padding-left: 16px; position: relative; }
ul.plist li::before { content: "—"; position: absolute; left: 0; color: var(--muted); }
ul.plist .dim { color: var(--muted); }
.foot-note { color: var(--muted); font-size: clamp(0.82rem, 1.12vw, 0.96rem); margin-top: 20px; max-width: 64ch; line-height: 1.55; }
svg { display: block; max-width: 100%; height: auto; }
.fig { margin: 2px 0 16px; }
.svg-ink { fill: var(--ink); } .svg-muted { fill: var(--muted); } .svg-accent { fill: var(--accent-ink); }
.svg-box { fill: var(--surface); stroke: var(--line); stroke-width: 1.5; }
.svg-box-hi { fill: var(--surface-2); stroke: var(--accent); stroke-width: 2.5; }
.svg-line { stroke: var(--muted); stroke-width: 1.8; fill: none; }
.svg-line-hi { stroke: var(--accent); stroke-width: 2.5; fill: none; }
text { font-family: -apple-system, "Segoe UI", system-ui, sans-serif; }
.bar-ui {
position: fixed; left: 0; right: 0; bottom: 0;
display: flex; align-items: center; justify-content: space-between;
padding: 9px clamp(16px, 3vw, 32px); gap: 16px;
border-top: 1px solid var(--line); background: var(--ground);
font-family: -apple-system, system-ui, sans-serif; font-size: 0.78rem; color: var(--muted);
}
.btn { background: var(--surface); border: 1px solid var(--line); color: var(--ink);
padding: 5px 13px; font-size: 0.8rem; cursor: pointer; border-radius: 4px; }
.btn:hover { border-color: var(--accent); }
.counter { font-variant-numeric: tabular-nums; letter-spacing: 0.03em; }
.where { font-weight: 600; color: var(--accent-ink); }
.nav { display: flex; gap: 10px; align-items: center; }
.progress { position: fixed; top: 0; left: 0; height: 3px; background: var(--accent); transition: width 0.18s ease; z-index: 5; }
@media print { .bar-ui, .progress { display: none; } }
</style>
<div class="progress" id="prog"></div>
<!-- ============ 1 · TITTEL ============ -->
<section class="slide" data-part="">
<div class="inner">
<div class="kicker">portfolio-optimiser · åpen kildekode</div>
<h1>Pengene inne i porteføljen</h1>
<p class="lead-sub">Et åpent rammeverk som leter etter kostnadsbesparelser i hvert enkelt prosjekt — og som ikke får lov til å godkjenne sine egne funn.</p>
<p class="foot-note">Tolv sider. Kjøringstallene underveis er hentet fra en eksempelkjøring som følger med i repoet — du kan gjenta den selv, uten kostnad, med tre kommandoer (side 11).</p>
</div>
</section>
<!-- ============ 2 · PROBLEMET ============ -->
<section class="slide" data-part="Problemet">
<div class="inner">
<div class="kicker">Problemet</div>
<h2>Besparelsene ligger inne i prosjekter som allerede er vedtatt</h2>
<div class="fig">
<svg viewBox="0 0 900 150" role="img" aria-label="En rekke vedtatte prosjekter, der noen bærer tiltak ingen har regnet på">
<rect class="svg-box" x="6" y="14" width="104" height="76" rx="3"/>
<rect class="svg-box" x="118" y="14" width="104" height="76" rx="3"/>
<rect class="svg-box" x="230" y="14" width="104" height="76" rx="3"/>
<rect class="svg-box" x="342" y="14" width="104" height="76" rx="3"/>
<rect class="svg-box" x="454" y="14" width="104" height="76" rx="3"/>
<rect class="svg-box" x="566" y="14" width="104" height="76" rx="3"/>
<rect class="svg-box" x="678" y="14" width="104" height="76" rx="3"/>
<rect class="svg-box" x="790" y="14" width="104" height="76" rx="3"/>
<rect x="130" y="58" width="80" height="22" rx="2" fill="var(--warn-bg)" stroke="var(--accent)" stroke-width="1.5"/>
<text x="170" y="74" font-size="11.5" font-weight="650" text-anchor="middle" fill="var(--warn-fg)">kr</text>
<rect x="466" y="58" width="80" height="22" rx="2" fill="var(--warn-bg)" stroke="var(--accent)" stroke-width="1.5"/>
<text x="506" y="74" font-size="11.5" font-weight="650" text-anchor="middle" fill="var(--warn-fg)">kr</text>
<rect x="690" y="58" width="80" height="22" rx="2" fill="var(--warn-bg)" stroke="var(--accent)" stroke-width="1.5"/>
<text x="730" y="74" font-size="11.5" font-weight="650" text-anchor="middle" fill="var(--warn-fg)">kr</text>
<text class="svg-muted" x="6" y="124" font-size="13">vedtatte prosjekter — hvert med sitt budsjett og sine kostlinjer</text>
<text class="svg-accent" x="894" y="124" font-size="13" font-weight="650" text-anchor="end">tiltak ingen har regnet på</text>
<path class="svg-line-hi" d="M 806 114 C 776 104 748 96 736 86" marker-end="url(#m2)"/>
<defs><marker id="m2" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 z" class="svg-accent"/></marker></defs>
</svg>
</div>
<p class="say">En portefølje er mange uavhengige prosjekter, hvert med sitt budsjett og sine kostlinjer. Erfaringen er at flere av dem rommer tiltak — en utskifting, en smartere styring, en annen metode — som ville spart penger uten å true det prosjektet skal levere.</p>
<p class="say dim">Besparelsene står ikke i noen rapport. De må letes fram, kostlinje for kostlinje — og det er letingen som er dyr, ikke tiltakene.</p>
</div>
</section>
<!-- ============ 3 · HVORFOR DE IKKE HENTES ============ -->
<section class="slide" data-part="Problemet">
<div class="inner">
<div class="kicker">Problemet</div>
<h2>Å lete koster tid, folk og fagkompetanse — per prosjekt</h2>
<div class="fig">
<svg viewBox="0 0 900 156" role="img" aria-label="Én fagperson mot en lang rekke prosjekter som blekner bortover">
<circle cx="60" cy="34" r="13" fill="none" stroke="var(--ink)" stroke-width="2"/>
<path d="M60 47 V84 M60 58 L40 72 M60 58 L80 72 M60 84 L46 112 M60 84 L74 112" fill="none" stroke="var(--ink)" stroke-width="2" stroke-linecap="round"/>
<text class="svg-muted" x="60" y="140" font-size="12.5" text-anchor="middle">én fagperson</text>
<g opacity="1"><rect class="svg-box" x="150" y="22" width="72" height="64" rx="3"/></g>
<g opacity="0.92"><rect class="svg-box" x="232" y="22" width="72" height="64" rx="3"/></g>
<g opacity="0.84"><rect class="svg-box" x="314" y="22" width="72" height="64" rx="3"/></g>
<g opacity="0.76"><rect class="svg-box" x="396" y="22" width="72" height="64" rx="3"/></g>
<g opacity="0.68"><rect class="svg-box" x="478" y="22" width="72" height="64" rx="3"/></g>
<g opacity="0.6"><rect class="svg-box" x="560" y="22" width="72" height="64" rx="3"/></g>
<g opacity="0.52"><rect class="svg-box" x="642" y="22" width="72" height="64" rx="3"/></g>
<g opacity="0.44"><rect class="svg-box" x="724" y="22" width="72" height="64" rx="3"/></g>
<g opacity="0.36"><rect class="svg-box" x="806" y="22" width="72" height="64" rx="3"/></g>
<path class="svg-line" d="M150 98 v6 H878 v-6"/>
<text class="svg-muted" x="514" y="126" font-size="13" text-anchor="middle">dager med lesing og regning — per prosjekt</text>
</svg>
</div>
<p class="say">Den som skal finne et slikt tiltak, må lese prosjektgrunnlaget, kjenne tiltakstypene og regne på tallene. Det er dager med arbeid for hvert eneste prosjekt — og det krever folk som allerede er opptatt med å levere.</p>
<p class="say dim">De fleste leter allerede. Det som mangler er ikke vilje, men en leting som skalerer — over hele porteføljen, igjen og igjen.</p>
</div>
</section>
<!-- ============ 4 · HVORFOR KI ALENE IKKE LØSER DET ============ -->
<section class="slide" data-part="Problemet">
<div class="inner">
<div class="kicker">Problemet</div>
<h2>En språkmodell gir deg gjerne et pent tall ingen har regnet på</h2>
<div class="fig">
<svg viewBox="0 0 900 184" role="img" aria-label="Et pent notat med et stort beløp, og to ubesvarte spørsmål som peker på det">
<rect class="svg-box" x="330" y="10" width="240" height="150" rx="4"/>
<rect x="352" y="30" width="180" height="8" rx="2" fill="var(--line)"/>
<rect x="352" y="46" width="196" height="8" rx="2" fill="var(--line)"/>
<rect x="352" y="62" width="150" height="8" rx="2" fill="var(--line)"/>
<text class="svg-ink" x="450" y="112" font-size="24" font-weight="700" text-anchor="middle">«Spar 2,1 mill.»</text>
<text class="svg-muted" x="450" y="138" font-size="11.5" text-anchor="middle">grunnlag: ikke vedlagt</text>
<text class="svg-muted" x="6" y="66" font-size="13">Hvilke kostlinjer hviler det på?</text>
<path class="svg-line" d="M 214 62 H 320" marker-end="url(#m4)"/>
<text class="svg-muted" x="894" y="118" font-size="13" text-anchor="end">Er beløpet i det hele tatt oppnåelig?</text>
<path class="svg-line" d="M 646 114 H 580" marker-end="url(#m4)"/>
<text class="svg-accent" x="450" y="180" font-size="13" font-weight="650" text-anchor="middle">et overbevisende notat er en påstand — ikke en besparelse</text>
<defs><marker id="m4" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 z" class="svg-muted"/></marker></defs>
</svg>
</div>
<p class="say">KI-modeller leser fort og skriver overbevisende — det er nettopp problemet. Et velskrevet notat som lover «spar 2,1 millioner», er ikke en besparelse; det er en påstand. Skal funn brukes i beslutninger, må hvert tall kunne etterprøves.</p>
</div>
</section>
<!-- ============ 5 · GREPET ============ -->
<section class="slide" data-part="Grepet">
<div class="inner">
<div class="kicker">Grepet</div>
<h2>Maskinen får foreslå — aldri godkjenne seg selv</h2>
<div class="fig">
<svg viewBox="0 0 900 230" role="img" aria-label="Forslaget går til to uavhengige kontroller før det når fagpersonen">
<rect class="svg-box" x="6" y="86" width="170" height="64" rx="3"/>
<text class="svg-ink" x="91" y="112" font-size="15" font-weight="700" text-anchor="middle">Forslaget</text>
<text class="svg-muted" x="91" y="132" font-size="12" text-anchor="middle">fra KI-debatten</text>
<path class="svg-line" d="M176 104 C 220 96 230 70 274 64" marker-end="url(#m5)"/>
<path class="svg-line" d="M176 132 C 220 140 230 172 274 178" marker-end="url(#m5)"/>
<rect x="280" y="20" width="330" height="88" rx="3" fill="var(--warn-bg)" stroke="var(--accent)" stroke-width="2.5"/>
<text x="445" y="46" font-size="15" font-weight="700" text-anchor="middle" fill="var(--warn-fg)">Regnekoden — blokkerer</text>
<text x="445" y="68" font-size="12" text-anchor="middle" fill="var(--warn-fg)">avstemmer kostlinjene mot prosjektets faktiske tall</text>
<text x="445" y="88" font-size="12" text-anchor="middle" fill="var(--warn-fg)">beregner taket for hva som er oppnåelig</text>
<rect class="svg-box" x="280" y="140" width="330" height="76" rx="3"/>
<text class="svg-ink" x="445" y="168" font-size="15" font-weight="700" text-anchor="middle">Utfordreren</text>
<text class="svg-muted" x="445" y="190" font-size="12" text-anchor="middle">en KI-rolle med motsatt oppdrag: angripe begrunnelsen</text>
<path class="svg-line" d="M612 64 C 660 64 672 100 700 112" marker-end="url(#m5)"/>
<path class="svg-line" d="M612 178 C 660 178 672 142 700 136" marker-end="url(#m5)"/>
<rect x="706" y="92" width="188" height="64" rx="3" fill="var(--ok-bg)" stroke="var(--ok-fg)" stroke-width="2.5"/>
<text x="800" y="114" font-size="15" font-weight="700" text-anchor="middle" fill="var(--ok-fg)">Fagpersonen</text>
<text x="800" y="132" font-size="11.5" text-anchor="middle" fill="var(--ok-fg)">ser bare det som</text>
<text x="800" y="147" font-size="11.5" text-anchor="middle" fill="var(--ok-fg)">overlevde begge</text>
<defs><marker id="m5" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 z" class="svg-muted"/></marker></defs>
</svg>
</div>
<p class="say">Hvert forslag må overleve to uavhengige kontroller: en <strong>deterministisk regnekode</strong> som ikke kan overtales, og en <strong>utfordrer</strong> som angriper resonnementet.</p>
<p class="say dim">I eksempelkjøringen virker det slik: påstanden på 2&nbsp;100&nbsp;000&nbsp;kr ble avvist fordi taket var 1&nbsp;769&nbsp;915&nbsp;kr. Det korrigerte forslaget — 445&nbsp;500&nbsp;kr — slapp gjennom. Avvisningen er ikke en feil; den er systemet som virker.</p>
</div>
</section>
<!-- ============ 6 · KUNNSKAPSBASEN ============ -->
<section class="slide" data-part="Slik virker det">
<div class="inner">
<div class="kicker">Slik virker det</div>
<h2>Grunnlaget er en kunnskapsbase fagfolk kan lese og rette</h2>
<div class="fig">
<svg viewBox="0 0 900 236" role="img" aria-label="En innholdsfortegnelse peker til fem dokumenter som også lenker til hverandre">
<rect class="svg-box-hi" x="6" y="80" width="150" height="70" rx="3"/>
<text class="svg-ink" x="81" y="108" font-size="14" font-weight="650" text-anchor="middle">Inngangen</text>
<text class="svg-muted" x="81" y="126" font-size="11" text-anchor="middle">innholdsfortegnelsen</text>
<rect class="svg-box" x="220" y="14" width="190" height="62" rx="3"/>
<text class="svg-ink" x="315" y="40" font-size="13.5" font-weight="650" text-anchor="middle">Prosjektet</text>
<text class="svg-muted" x="315" y="58" font-size="11.5" text-anchor="middle">kostlinjer og rammer</text>
<rect class="svg-box" x="440" y="14" width="190" height="62" rx="3"/>
<text class="svg-ink" x="535" y="40" font-size="13.5" font-weight="650" text-anchor="middle">Tiltakstypene</text>
<text class="svg-muted" x="535" y="58" font-size="11.5" text-anchor="middle">hva som pleier å virke</text>
<rect class="svg-box" x="660" y="14" width="190" height="62" rx="3"/>
<text class="svg-ink" x="755" y="40" font-size="13.5" font-weight="650" text-anchor="middle">Erfaringstall</text>
<text class="svg-muted" x="755" y="58" font-size="11.5" text-anchor="middle">tidligere dommer</text>
<rect class="svg-box" x="330" y="150" width="190" height="62" rx="3"/>
<text class="svg-ink" x="425" y="176" font-size="13.5" font-weight="650" text-anchor="middle">Fagkilder</text>
<text class="svg-muted" x="425" y="194" font-size="11.5" text-anchor="middle">håndbøker og normtall</text>
<rect class="svg-box" x="550" y="150" width="190" height="62" rx="3"/>
<text class="svg-ink" x="645" y="176" font-size="13.5" font-weight="650" text-anchor="middle">Metoden</text>
<text class="svg-muted" x="645" y="194" font-size="11.5" text-anchor="middle">hvordan det regnes</text>
<path class="svg-line" d="M156 96 C 180 88 195 55 216 45" marker-end="url(#m6)"/>
<path class="svg-line" d="M156 104 C 280 92 380 58 436 46" marker-end="url(#m6)"/>
<path class="svg-line" d="M156 110 C 340 108 550 66 656 50" marker-end="url(#m6)"/>
<path class="svg-line" d="M156 122 C 220 140 270 168 326 178" marker-end="url(#m6)"/>
<path class="svg-line" d="M156 128 C 300 168 440 190 546 188" marker-end="url(#m6)"/>
<path class="svg-line" d="M412 45 H 436" stroke-dasharray="5 4"/>
<path class="svg-line" d="M632 45 H 656" stroke-dasharray="5 4"/>
<path class="svg-line" d="M535 78 C 510 110 470 122 445 148" stroke-dasharray="5 4"/>
<path class="svg-line" d="M755 78 C 730 112 690 124 662 148" stroke-dasharray="5 4"/>
<text class="svg-muted" x="450" y="232" font-size="12.5" text-anchor="middle">fem dokumenter i eksempelet — alle i klartekst, alle mulige å rette</text>
<defs><marker id="m6" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 z" class="svg-muted"/></marker></defs>
</svg>
</div>
<p class="say">Maskinen graver ikke i løse dokumenthauger. Den navigerer en <strong>kunnskapsbase</strong> — en wiki av korte tekstdokumenter som lenker til hverandre. I eksempelet er det fem; i et virkelig prosjekt kan det være hundrevis.</p>
<p class="say dim">Alt maskinen bygger på, står i klartekst. En fagperson kan åpne basen, se nøyaktig hva systemet «vet» — og rette det som er feil. Grunnlaget er ditt, ikke modellens.</p>
</div>
</section>
<!-- ============ 7 · ARKITEKTUREN ============ -->
<section class="slide" data-part="Slik virker det">
<div class="inner">
<div class="kicker">Slik virker det</div>
<h2>Fire lag — og bare det godkjente går tilbake</h2>
<div class="fig">
<svg viewBox="0 0 900 252" role="img" aria-label="Fire lag fra grunnlag til dom, med en returpil for godkjent kunnskap">
<rect x="40" y="10" width="700" height="48" rx="3" fill="var(--surface-2)" stroke="var(--line)" stroke-width="1.5"/>
<text class="svg-ink" x="58" y="40" font-size="14.5" font-weight="650">1 · Grunnlaget</text>
<text class="svg-muted" x="724" y="40" font-size="12" text-anchor="end">kunnskapsbasen — dokumentene fagfolk eier</text>
<rect class="svg-box" x="40" y="72" width="700" height="48" rx="3"/>
<text class="svg-ink" x="58" y="102" font-size="14.5" font-weight="650">2 · Forslaget</text>
<text class="svg-muted" x="724" y="102" font-size="12" text-anchor="end">KI-modeller foreslår og utfordrer hverandre</text>
<rect x="40" y="134" width="700" height="48" rx="3" fill="var(--warn-bg)" stroke="var(--accent)" stroke-width="2"/>
<text x="58" y="164" font-size="14.5" font-weight="650" fill="var(--warn-fg)">3 · Kontrollen</text>
<text x="724" y="164" font-size="12" text-anchor="end" fill="var(--warn-fg)">regnekoden blokkerer alt den ikke kan bekrefte</text>
<rect x="40" y="196" width="700" height="48" rx="3" fill="var(--ok-bg)" stroke="var(--ok-fg)" stroke-width="2"/>
<text x="58" y="226" font-size="14.5" font-weight="650" fill="var(--ok-fg)">4 · Dommen</text>
<text x="724" y="226" font-size="12" text-anchor="end" fill="var(--ok-fg)">fagpersonen dømmer det som står igjen</text>
<path class="svg-line" d="M390 58 V 66" marker-end="url(#m7)"/>
<path class="svg-line" d="M390 120 V 128" marker-end="url(#m7)"/>
<path class="svg-line" d="M390 182 V 190" marker-end="url(#m7)"/>
<path class="svg-line-hi" d="M 744 220 H 806 Q 826 220 826 200 V 54 Q 826 34 806 34 H 750" marker-end="url(#m7a)"/>
<text class="svg-accent" x="838" y="118" font-size="12" font-weight="650">godkjent</text>
<text class="svg-accent" x="838" y="134" font-size="12" font-weight="650">kunnskap</text>
<text class="svg-accent" x="838" y="150" font-size="12" font-weight="650">tilbake</text>
<defs>
<marker id="m7" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 z" class="svg-muted"/></marker>
<marker id="m7a" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 z" class="svg-accent"/></marker>
</defs>
</svg>
</div>
<p class="say">Grunnlaget mater forslagsarbeidet. Kontrollaget feller alt som ikke tåler etterregning. Fagpersonen dømmer det som står igjen. Og returpilen er poenget: en godkjent dom skrives tilbake i kunnskapsbasen, så neste kjøring starter klokere enn den forrige.</p>
</div>
</section>
<!-- ============ 8 · DE ÅTTE STEGENE ============ -->
<section class="slide" data-part="Slik virker det">
<div class="inner">
<div class="kicker">Slik virker det</div>
<h2>Arbeidsprosessen: åtte steg, med menneskene i begge ender</h2>
<div class="fig">
<svg viewBox="0 0 900 268" role="img" aria-label="Åtte steg på rad, med bånd som viser hvem som utfører hvert steg">
<rect x="6" y="14" width="328" height="34" rx="3" fill="var(--surface-2)" stroke="var(--line)" stroke-width="1.5"/>
<text class="svg-muted" x="170" y="36" font-size="14" font-weight="650" text-anchor="middle">KI-modellene arbeider</text>
<rect x="342" y="14" width="104" height="34" rx="3" fill="var(--warn-bg)" stroke="var(--accent)" stroke-width="2"/>
<text x="394" y="36" font-size="14" font-weight="650" text-anchor="middle" fill="var(--warn-fg)">Regnekoden</text>
<rect x="454" y="14" width="216" height="34" rx="3" fill="var(--surface-2)" stroke="var(--line)" stroke-width="1.5"/>
<text class="svg-muted" x="562" y="36" font-size="14" font-weight="650" text-anchor="middle">KI + regnekode</text>
<rect x="678" y="14" width="216" height="34" rx="3" fill="var(--ok-bg)" stroke="var(--ok-fg)" stroke-width="2"/>
<text x="786" y="36" font-size="14" font-weight="650" text-anchor="middle" fill="var(--ok-fg)">Fagpersonen</text>
<g font-size="13">
<rect class="svg-box" x="6" y="70" width="104" height="76" rx="3"/>
<text class="svg-accent" x="58" y="96" font-size="19" font-weight="700" text-anchor="middle">1</text>
<text class="svg-muted" x="58" y="122" text-anchor="middle">Les</text>
<text class="svg-muted" x="58" y="138" text-anchor="middle">grunnlaget</text>
<rect class="svg-box" x="118" y="70" width="104" height="76" rx="3"/>
<text class="svg-accent" x="170" y="96" font-size="19" font-weight="700" text-anchor="middle">2</text>
<text class="svg-muted" x="170" y="122" text-anchor="middle">Foreslå</text>
<text class="svg-muted" x="170" y="138" text-anchor="middle">tiltak</text>
<rect class="svg-box" x="230" y="70" width="104" height="76" rx="3"/>
<text class="svg-accent" x="282" y="96" font-size="19" font-weight="700" text-anchor="middle">3</text>
<text class="svg-muted" x="282" y="122" text-anchor="middle">Utfordre</text>
<text class="svg-muted" x="282" y="138" text-anchor="middle">begrunnelsen</text>
<rect x="342" y="70" width="104" height="76" rx="3" fill="var(--warn-bg)" stroke="var(--accent)" stroke-width="2.5"/>
<text x="394" y="96" font-size="19" font-weight="700" text-anchor="middle" fill="var(--warn-fg)">4</text>
<text x="394" y="122" text-anchor="middle" fill="var(--warn-fg)">Regn etter</text>
<text x="394" y="138" text-anchor="middle" fill="var(--warn-fg)">— blokkerer</text>
<rect class="svg-box" x="454" y="70" width="104" height="76" rx="3"/>
<text class="svg-accent" x="506" y="96" font-size="19" font-weight="700" text-anchor="middle">5</text>
<text class="svg-muted" x="506" y="122" text-anchor="middle">Forbedre</text>
<text class="svg-muted" x="506" y="138" text-anchor="middle">og prøv igjen</text>
<rect class="svg-box" x="566" y="70" width="104" height="76" rx="3"/>
<text class="svg-accent" x="618" y="96" font-size="19" font-weight="700" text-anchor="middle">6</text>
<text class="svg-muted" x="618" y="122" text-anchor="middle">Lever</text>
<text class="svg-muted" x="618" y="138" text-anchor="middle">utfallet</text>
<rect x="678" y="70" width="104" height="76" rx="3" fill="var(--ok-bg)" stroke="var(--ok-fg)" stroke-width="2.5"/>
<text x="730" y="96" font-size="19" font-weight="700" text-anchor="middle" fill="var(--ok-fg)">7</text>
<text x="730" y="122" text-anchor="middle" fill="var(--ok-fg)">Døm</text>
<text x="730" y="138" text-anchor="middle" fill="var(--ok-fg)">resultatet</text>
<rect x="790" y="70" width="104" height="76" rx="3" fill="var(--ok-bg)" stroke="var(--ok-fg)" stroke-width="2.5"/>
<text x="842" y="96" font-size="19" font-weight="700" text-anchor="middle" fill="var(--ok-fg)">8</text>
<text x="842" y="122" text-anchor="middle" fill="var(--ok-fg)">Lagre det</text>
<text x="842" y="138" text-anchor="middle" fill="var(--ok-fg)">godkjente</text>
</g>
<path class="svg-line-hi" d="M506 152 V 176 Q 506 192 486 192 H 190 Q 170 192 170 176 V 152" marker-end="url(#m8)"/>
<text class="svg-accent" x="338" y="212" font-size="13" font-weight="650" text-anchor="middle">avvisningen sendes tilbake — begrenset antall forsøk</text>
<path class="svg-line-hi" d="M842 152 V 232 Q 842 250 820 250 H 80 Q 58 250 58 232 V 152" marker-end="url(#m8)"/>
<text class="svg-accent" x="450" y="266" font-size="13" font-weight="650" text-anchor="middle">det fagpersonen godkjente blir grunnlag for neste kjøring</text>
<defs><marker id="m8" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 z" class="svg-accent"/></marker></defs>
</svg>
</div>
<p class="say">Grønt er der fagfolkene står. Gult er regnekoden som eier tallene. KI-modellene gjør lese- og skrivearbeidet imellom — og de to pilene tilbake er de eneste veiene noe går i retur.</p>
</div>
</section>
<!-- ============ 9 · MENNESKENE ============ -->
<section class="slide" data-part="Menneskene">
<div class="inner">
<div class="kicker">Menneskene</div>
<h2>Dommen din blir systemets hukommelse</h2>
<div class="fig">
<svg viewBox="0 0 900 212" role="img" aria-label="En port slipper godkjente dommer inn i kunnskapsbasen og stopper rå maskinoutput">
<rect x="6" y="28" width="250" height="56" rx="3" fill="var(--ok-bg)" stroke="var(--ok-fg)" stroke-width="2"/>
<text x="131" y="52" font-size="14" font-weight="650" text-anchor="middle" fill="var(--ok-fg)">Dommen din — godkjent</text>
<text x="131" y="70" font-size="11.5" text-anchor="middle" fill="var(--ok-fg)">med begrunnelse, sporbar</text>
<rect x="6" y="124" width="250" height="56" rx="3" fill="var(--bad-bg)" stroke="var(--bad-fg)" stroke-width="2"/>
<text x="131" y="148" font-size="14" font-weight="650" text-anchor="middle" fill="var(--bad-fg)">Rå maskinoutput</text>
<text x="131" y="166" font-size="11.5" text-anchor="middle" fill="var(--bad-fg)">forslag ingen har dømt</text>
<rect x="452" y="16" width="8" height="58" fill="var(--surface-2)" stroke="var(--accent)" stroke-width="1.5"/>
<rect x="452" y="104" width="8" height="88" fill="var(--surface-2)" stroke="var(--accent)" stroke-width="1.5"/>
<path class="svg-line-hi" d="M258 56 C 360 58 400 86 468 88 H 634" marker-end="url(#m9)"/>
<path class="svg-line" d="M258 152 H 434" marker-end="url(#m9m)"/>
<path d="M441 144 l14 16 M455 144 l-14 16" stroke="var(--bad-fg)" stroke-width="2.5" fill="none"/>
<rect class="svg-box-hi" x="644" y="60" width="250" height="58" rx="3"/>
<text class="svg-ink" x="769" y="84" font-size="14.5" font-weight="650" text-anchor="middle">Kunnskapsbasen</text>
<text class="svg-muted" x="769" y="102" font-size="12" text-anchor="middle">neste kjøring starter her</text>
<text class="svg-muted" x="450" y="208" font-size="12.5" text-anchor="middle">porten er lukket som utgangspunkt — det som ikke er dømt, kommer ikke inn</text>
<defs>
<marker id="m9" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 z" class="svg-accent"/></marker>
<marker id="m9m" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 z" class="svg-muted"/></marker>
</defs>
</svg>
</div>
<p class="say">Når du godkjenner eller avviser et utfall, forsvinner ikke vurderingen i et referat. En port skriver den inn i kunnskapsbasen — og porten er lukket for alt annet: rå maskinoutput slipper aldri inn.</p>
<p class="say dim">I eksempelkjøringen: første kjøring hentet 0 tidligere dommer for kandidaten. Etter én godkjenning og ett driftsnotat i innboksen hentet neste kjøring 3 — og begge de målte realiseringsgradene (0,79 og 0,66) var på plass i dens hypotesegrunnlag.</p>
</div>
</section>
<!-- ============ 10 · VERDIEN ============ -->
<section class="slide" data-part="Verdien">
<div class="inner">
<div class="kicker">Verdien</div>
<h2>Hva det gir, i størrelsesorden</h2>
<div class="fig">
<svg viewBox="0 0 900 212" role="img" aria-label="Fire søyler: kostlinja, den avviste påstanden, det beregnede taket og den validerte besparelsen">
<text class="svg-muted" x="250" y="30" font-size="13" text-anchor="end">kostlinja tiltaket rører</text>
<rect x="260" y="8" width="560" height="34" fill="var(--surface-2)" stroke="var(--line)" stroke-width="1.5"/>
<text class="svg-ink" x="812" y="30" font-size="13" font-weight="650" text-anchor="end">4 386 150 kr</text>
<text class="svg-muted" x="250" y="80" font-size="13" text-anchor="end">først påstått — avvist</text>
<rect x="260" y="58" width="268" height="34" fill="var(--bad-bg)" stroke="var(--bad-fg)" stroke-width="1.5"/>
<text x="536" y="80" font-size="13" font-weight="650" fill="var(--bad-fg)">2 100 000 kr</text>
<text class="svg-muted" x="250" y="130" font-size="13" text-anchor="end">taket regnekoden fant</text>
<rect x="260" y="108" width="226" height="34" fill="var(--warn-bg)" stroke="var(--accent)" stroke-width="1.5"/>
<text x="494" y="130" font-size="13" font-weight="650" fill="var(--warn-fg)">1 769 915 kr</text>
<text class="svg-muted" x="250" y="180" font-size="13" text-anchor="end">validert besparelse</text>
<rect x="260" y="158" width="57" height="34" fill="var(--ok-bg)" stroke="var(--ok-fg)" stroke-width="1.5"/>
<text x="325" y="180" font-size="13" font-weight="650" fill="var(--ok-fg)">445 500 kr — 10,2 % av linja</text>
<text class="svg-muted" x="450" y="208" font-size="12.5" text-anchor="middle">tallene er fra eksempelkjøringen — utskriften er byte-låst fasit i repoet</text>
</svg>
</div>
<p class="say">Dette er <strong>ett</strong> prosjekt og <strong>én</strong> kostlinje. Verdien av rammeverket er ikke ett enkelt tall, men tre egenskaper: letingen blir nesten gratis per prosjekt, bare etterprøvbare tall når fram til beslutning, og hver dom gjør neste leting bedre.</p>
<p class="say dim">Størrelsesorden, ikke løfte: hva din portefølje rommer, vet ingen før letingen er gjort. Det nye er at selve letingen er blitt billig.</p>
</div>
</section>
<!-- ============ 11 · KOM I GANG ============ -->
<section class="slide" data-part="Kom i gang">
<div class="inner">
<div class="kicker">Kom i gang</div>
<h2>Tre kommandoer, null kostnad</h2>
<div class="cmd">git clone https://git.fromaitochitta.com/open/portfolio-optimiser.git
cd portfolio-optimiser &amp;&amp; uv sync
uv run portfolio-optimiser-demo</div>
<p class="say">Demoen kjører hele sløyfa uten modell og uten nettverk: to kjøringer adskilt av en fagdom, med skriptede agent-svar — og den sier det selv, i klartekst, øverst i utskriften. Det du ser er dataflyten, regneporten og at læringen faktisk når fram til neste kjøring.</p>
<p class="foot-note">Krever Python ≥ 3.10 og <code>uv</code>. Åpen kildekode (MIT), bygget på Microsoft Agent Framework. README-en viser resten — også hvordan du bygger en kunnskapsbase for et eget prosjekt.</p>
</div>
</section>
<!-- ============ 12 · ÆRLIG STATUS ============ -->
<section class="slide" data-part="Ærlig status">
<div class="inner">
<div class="kicker">Ærlig status</div>
<h2>Hva som er bevist — og hva som ikke er</h2>
<div class="cards">
<div class="card good">
<span class="t">Bevist — målt, ikke ment</span>
<ul class="plist">
<li>Hele sløyfa kjører ende til ende offline, og læringen når fram på begge tidsskalaer. Utskriften er byte-låst som fasit i repoet.</li>
<li>Regneporten feller reelt: 2&nbsp;100&nbsp;000-påstanden avvises og 445&nbsp;500 valideres — i hver kjøring, hver gang.</li>
<li>846 automatiske tester grønne (4 hoppes over), målt 14. august 2026.</li>
<li>Installasjonen er målt, ikke antatt: dokumentert kommando, 65 pakker, exit 0.</li>
</ul>
</div>
<div class="card risk">
<span class="t">Ikke bevist</span>
<ul class="plist">
<li>Rammeverket har <strong>aldri kjørt mot en levende språkmodell</strong>. Hvert forslag og hver dom til nå er skriptede stand-ins — demoen sier det selv, øverst i hver kjøring.</li>
<li>Om en ekte modell foreslår godt og dømmer klokt, er et åpent spørsmål. Ingen målt gevinst fra en virkelig portefølje ennå.</li>
</ul>
</div>
</div>
<p class="say dim" style="margin-top:18px">Det som mangler, er nøyaktig det en første pilot finnes for å måle. Grunnmuren er bygget for at den målingen skal kunne stoles på.</p>
<p class="foot-note">Kjøringstallene i denne presentasjonen er byte-låst fasit: <code>tests/golden/demo-transcript.stdout</code>.</p>
</div>
</section>
<div class="bar-ui">
<span>← → for å bla · F for fullskjerm</span>
<span class="where" id="where"></span>
<span class="nav"><button class="btn" id="prev"></button><span class="counter" id="count"></span><button class="btn" id="next"></button></span>
</div>
<script>
(function () {
var slides = Array.prototype.slice.call(document.querySelectorAll('.slide'));
var i = 0;
var count = document.getElementById('count');
var where = document.getElementById('where');
var prog = document.getElementById('prog');
function show(n) {
i = Math.max(0, Math.min(slides.length - 1, n));
slides.forEach(function (s, k) { s.classList.toggle('active', k === i); });
count.textContent = (i + 1) + ' / ' + slides.length;
where.textContent = slides[i].getAttribute('data-part') || '';
prog.style.width = ((i + 1) / slides.length * 100) + '%';
try { location.hash = String(i + 1); } catch (e) {}
}
function go(d) { show(i + d); }
document.addEventListener('keydown', function (e) {
var k = e.key;
if (k === 'ArrowRight' || k === 'ArrowDown' || k === 'PageDown' || k === ' ' || k === 'Enter') { go(1); e.preventDefault(); }
else if (k === 'ArrowLeft' || k === 'ArrowUp' || k === 'PageUp' || k === 'Backspace') { go(-1); e.preventDefault(); }
else if (k === 'Home') { show(0); e.preventDefault(); }
else if (k === 'End') { show(slides.length - 1); e.preventDefault(); }
else if (k === 'f' || k === 'F') {
if (document.fullscreenElement) { document.exitFullscreen(); }
else if (document.documentElement.requestFullscreen) { document.documentElement.requestFullscreen(); }
e.preventDefault();
}
});
document.getElementById('next').addEventListener('click', function () { go(1); });
document.getElementById('prev').addEventListener('click', function () { go(-1); });
var start = null;
document.addEventListener('touchstart', function (e) { start = e.changedTouches[0].clientX; }, {passive: true});
document.addEventListener('touchend', function (e) {
if (start === null) return;
var dx = e.changedTouches[0].clientX - start;
if (Math.abs(dx) > 55) { go(dx < 0 ? 1 : -1); }
start = null;
}, {passive: true});
var h = parseInt((location.hash || '').replace('#', ''), 10);
show(isNaN(h) ? 0 : h - 1);
})();
</script>