feat: add public tool preview pages with realistic samples

Each landing card now links to preview.php?tool=SLUG — a dedicated
public page with an expanded pitch, 4 capability bullets, and a
realistic Norwegian-language sample input+output for all 7 tools.

- preview.php — new public page (no auth required), switch-driven content
- includes/tool-svgs.php — extracted $toolSvgs into shared include
- index.php — require tool-svgs.php, card href → preview.php?tool=SLUG
- assets/css/tools.css — lt-preview-* component styles appended

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 13:08:46 +02:00
parent c350750b7e
commit 849a7cf434
4 changed files with 571 additions and 18 deletions
+237
View File
@@ -0,0 +1,237 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/bootstrap.php';
require_once __DIR__ . '/includes/tool-svgs.php';
$uiLang = dbnToolsCurrentLanguage();
$tools = dbnToolsLaunchedTools($uiLang);
$slug = $_GET['tool'] ?? '';
if (!array_key_exists($slug, $tools)) {
header('Location: index.php');
exit;
}
$tool = $tools[$slug];
$returnPath = '/';
$toolsLogin = 'https://dobetternorge.no/tools-login.php?return=' . urlencode($returnPath);
// ── Per-tool preview content ──────────────────────────────────────────────────
$previews = [
'transcribe' => [
'pitch' => 'Upload any recording of a meeting, phone call, or court hearing. The AI transcribes with Norwegian legal vocabulary, separates speakers automatically, and delivers timestamped text you can search, quote, and submit as evidence.',
'features' => [
'Speaker diarization — Saksbehandler, Forelder, Advokat',
'Norwegian legal vocabulary and statute citations',
'Sentence-level timestamps with export to PDF or plain text',
'Processes entirely in memory — audio is never stored',
],
'scenario_label' => 'Sample input',
'scenario' => 'familiesamtale_barnevernet_14mar2025.mp3 &mdash; 23:47 &mdash; Statutory review meeting',
'output_label' => 'Transcript output',
'output' => "[00:00 — Saksbehandler] Jeg vil informere om at dette møtet dokumenteres jf.\n forvaltningsloven § 11d. Formålet er å gjennomgå tiltaksplanen.\n\n[00:18 — Forelder] Vi ønsker å forstå hva som er grunnlaget for bekymringene\n dere fremdeles har, og hva som skal til for å avslutte saken.\n\n[00:31 — Saksbehandler] Bekymringsmeldingen kom inn 14. mars. Vi gjennomførte\n hjemmebesøk 2. april. Tiltaksplanen løper til 30. juni.\n\n[00:48 — Advokat] For protokollen: klienten fikk ikke skriftlig varsel innen\n fristen i forvaltningsloven § 16.",
],
'timeline' => [
'pitch' => 'Paste case notes or upload documents and Timeline extracts every date, hearing, and procedural deadline into a sortable chronology. Flag gaps in due process and see your entire case at a glance.',
'features' => [
'Detects absolute and relative dates ("tre uker etter vedtaket")',
'Category tags: klage, rettsak, hjemmebesøk, frist',
'Missed statutory deadlines highlighted in red',
'Export to CSV or printable table',
],
'scenario_label' => 'Sample input',
'scenario' => 'Four case documents pasted — 3,200 words of notes, letters, and decision excerpts',
'output_label' => 'Timeline output',
'output' => "DATE EVENT SOURCE FLAG\n─────────────────────────────────────────────────────────────────────────────\n14.03.2025 Bekymringsmelding mottatt Barnevernets brev\n02.04.2025 Hjemmebesøk gjennomført Tiltaksplan\n16.04.2025 Klagefrist utløpt Fvl. § 29 ⚠ Svar\n (Klage innsendt 23.04 — 3 dager for sent\n etter frist)\n15.05.2025 Fylkesnemnda berammet Vedtak 2025-882\n30.06.2025 Tiltaksplan utløper Tiltaksplan → Ny\n vurdering",
],
'redact' => [
'pitch' => 'Before sharing documents with courts, advocates, or public forums, Redact removes all personal identifiers automatically. Nothing is sent to third-party AI — redaction runs locally on our servers.',
'features' => [
'Fødselsnummer (11-digit national ID) detection',
'NER-based name and organisation removal',
'Addresses, postcodes, phone numbers, and email addresses',
'Reversible audit log — restore original on request',
],
'scenario_label' => 'Document excerpt (before)',
'scenario' => 'Barnet Sofie Kristine Lindstr&oslash;m, f.&nbsp;14.07.2017, og hennes mor Ingrid Mari Lindstr&oslash;m (fnr.&nbsp;14095678234), bosatt Torggata&nbsp;14B,&nbsp;0181&nbsp;Oslo, ble kontaktet av saksbehandler Morten Aas den 6.&nbsp;mai.',
'output_label' => 'After redaction',
'output' => "Barnet [NAVN], f. [DATO], og hennes mor [NAVN]\n(fnr. [FNR]), bosatt [ADRESSE], ble kontaktet\nav saksbehandler [NAVN] den 6. mai.\n\n─────────────────────────────────────\nRedacted: 5 names · 1 fødselsnummer\n 1 address · 1 date of birth",
],
'barnevernet' => [
'pitch' => 'Upload any Barnevernet document — decision letter, tiltaksplan, or omsorgsovertakelse — and the analyzer reads it from your perspective, cross-referencing against 1,731 indexed tribunal decisions to surface procedural violations you can challenge.',
'features' => [
'§4-12 / §4-6(2) / §4-21 procedural analysis',
'Cross-reference against FNV tribunal corpus (1,731 decisions)',
'ECHR Article 8 pattern matching and citation',
'Exportable flagged report with paragraph references',
],
'scenario_label' => 'Document uploaded',
'scenario' => 'Vedtak om omsorgsovertakelse — Bydel&nbsp;&Aring;rstad, 14.&nbsp;januar&nbsp;2025 &mdash; 6 pages',
'output_label' => 'Analysis output',
'output' => "ANALYSE — Omsorgsovertakelse §4-12\nRøde flagg funnet: 3\n\n🔴 Utilstrekkelig begrunnelse\n Vedtaket påberoper §4-12(1)(c) uten å spesifisere\n terskelen for \"alvorlige mangler\" — jf. FNV-2023-192-OSL\n der identisk formulering ble opphevet av Høyesterett.\n\n🟡 Tilbakeføring ikke vurdert\n Ingen §4-21-vurdering er inkludert. Strand Lobben v.\n Norway (klagenr. 37283/13 ¶206) krever at staten\n treffer aktive tiltak mot gjenforening fra dag én.\n\n🟡 Foreldrehøring mangler\n Ingen dokumentasjon på at foreldrene ble hørt\n i forkant av vedtaket, jf. forvaltningsloven §17.",
],
'advocate' => [
'pitch' => 'Choose whether you represent a parent, child, foster carer, or municipality, describe the facts, and Advocate drafts a source-grounded legal brief — citing directly from Norwegian law, ECHR rulings, and indexed tribunal decisions.',
'features' => [
'Four representable roles: parent, child, foster carer, municipality',
'ECHR Grand Chamber citations with paragraph references',
'Lovdata statute cross-references (Barneloven, Barnevernloven)',
'Adjustable argument weight and brief length',
],
'scenario_label' => 'Role and facts',
'scenario' => 'Role: Forelderens representant &mdash; Facts: Akuttplassering etter §4-6(2) iverksatt 14. januar 2025 uten forutgående varsel. Barnet er 7 år. Kommunen anfører §4-12(1)(d).',
'output_label' => 'Draft brief excerpt',
'output' => "PARTSINLEGG — Forelderens perspektiv\nSak: Akuttplassering §4-6(2), januar 2025\n\nSøkeren anfører at akuttplasseringen av 14. januar 2025\nutgjørde en krenkelse av EMK artikkel 8, slik storkammeret\nfastla i Strand Lobben v. Norway (klagenr. 37283/13, ¶206).\n\nProporsjonalitetskravet forutsetter at myndighetene\ndokumenterer: (i) at risikoen var umiddelbar og konkret;\n(ii) at mindre inngripende tiltak ble reelt vurdert; og\n(iii) at vedtaket inneholdt en konkret gjenforeningsplan.\n\nKommunen har ikke fremlagt slik dokumentasjon.",
],
'deep-research' => [
'pitch' => 'Ask a complex legal question and the research agent expands it into parallel search angles, queries three slices of the legal corpus simultaneously, then synthesizes a fully cited brief — more thorough than any single keyword search.',
'features' => [
'Auto-expands your question into 35 research angles',
'Parallel corpus queries across statutes, ECHR, and tribunal decisions',
'Contradiction detection between sources',
'Cited synthesis with clickable source links',
],
'scenario_label' => 'Research query',
'scenario' => 'Kan midlertidig fosterhjemsplassering bli permanent uten ny saksbehandling?',
'output_label' => 'Synthesis excerpt (3 of 5 angles completed)',
'output' => "RESEARCH ANGLES\n ① §4-21 vilkår for opphør av omsorgsovertakelse\n ② ECHR art. 8 — statens gjenforeningsplikt\n ③ Fylkesnemnda-praksis 20202025 ✓ 14 relevante avgjørelser\n\nSYNTHESIS\nEtter §4-21 første ledd kan omsorgsovertakelse bare\nopprettholdes dersom tilbakeføring vil utsette barnet for\nreell risiko for skadevirkninger av betydning.\n\nStorkammeret presiserte i K.O. og V.M. v. Norway\n(klagenr. 64808/16, ¶67) at langvarig plassering ikke i seg\nselv legitimerer varig omsorgsovertakelse — ny og selvstendig\nvurdering er påkrevet.",
],
'corpus' => [
'pitch' => 'Browse the legal knowledge base that powers all other tools. Inspect indexed sources by category, check retrieval health, run test queries against specific legal slices, and verify what precedents are available before launching a full research session.',
'features' => [
'219,847 indexed passages across 5 source categories',
'Source browser with document count and last-indexed date',
'Retrieval health dashboard with latency and recall metrics',
'Test-query interface against individual corpus slices',
],
'scenario_label' => 'Corpus snapshot',
'scenario' => 'Full corpus status &mdash; last updated 16 May 2026',
'output_label' => 'Corpus dashboard',
'output' => "Korpusstatus: Frisk ✔ — sist oppdatert 16. mai 2026\n\nKILDE DOKUMENTER PASSASJER\n──────────────────────────────────────────────────────────\nBarnevernet-avgjørelser (FNV) 1 731 87 420\nECHR (norske saker) 23 9 204\nBarneloven + Barnevernloven 4 lover 6 112\nHøyesterett 142 18 903\nAkademiske kommentarer 38 4 208\n──────────────────────────────────────────────────────────\nTotalt 219 847\n\nEmbedding model : nomic-embed-text (768-dim)\nVector store : Qdrant v1.17.0 @ 10.0.2.10:6333\nAvg query p95 : 142 ms",
],
];
$preview = $previews[$slug];
?>
<!doctype html>
<html lang="<?= htmlspecialchars($uiLang) ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars($tool['label']) ?> &mdash; Do Better Norge Legal Tools</title>
<meta name="description" content="<?= htmlspecialchars($tool['description']) ?>">
<meta name="robots" content="index, follow">
<meta name="theme-color" content="#00205B">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@400;600;700&family=Roboto:wght@300;400;500;700&family=IBM+Plex+Mono:wght@400;700&display=swap">
<link rel="stylesheet" href="assets/css/tools.css">
</head>
<body class="lt-landing lt-preview-page">
<header class="lt-nav">
<a href="https://dobetternorge.no" class="lt-nav__brand">
<picture>
<source srcset="assets/images/logo-header.webp" type="image/webp">
<img class="lt-nav__logo" src="assets/images/logo-header.png" alt="Do Better Norge" width="140" height="36" loading="eager">
</picture>
<span class="lt-nav__badge">Legal Tools</span>
</a>
<div class="lt-nav__right">
<nav class="shell-lang-switcher" aria-label="Language">
<?php foreach (dbnToolsSupportedLanguages() as $langCode): ?>
<a href="?tool=<?= htmlspecialchars($slug) ?>&amp;lang=<?= htmlspecialchars($langCode) ?>" class="<?= $langCode === $uiLang ? 'is-active' : '' ?>"><?= htmlspecialchars(dbnToolsLanguageLabel($langCode)) ?></a>
<?php endforeach; ?>
</nav>
<a href="index.php#access" class="lt-nav__cta">Get access</a>
</div>
</header>
<main>
<section class="lt-preview-hero">
<div class="lt-preview-hero__inner">
<div class="lt-preview-hero__art" aria-hidden="true">
<?= $toolSvgs[$slug] ?? '' ?>
</div>
<div class="lt-preview-hero__text">
<a href="index.php#tools" class="lt-preview-back">&larr; All tools</a>
<span class="lt-preview-badge"><?= htmlspecialchars($tool['badge']) ?></span>
<h1 class="lt-preview-title"><?= htmlspecialchars($tool['label']) ?></h1>
<p class="lt-preview-sub"><?= htmlspecialchars($tool['description']) ?></p>
<p class="lt-preview-pitch"><?= htmlspecialchars($preview['pitch']) ?></p>
</div>
</div>
</section>
<section class="lt-preview-features">
<div class="lt-preview-features__inner">
<h2 class="lt-preview-features__heading">What it does</h2>
<ul class="lt-preview-features__list">
<?php foreach ($preview['features'] as $feat): ?>
<li class="lt-preview-features__item">
<span class="lt-preview-features__dot" aria-hidden="true"></span>
<?= htmlspecialchars($feat) ?>
</li>
<?php endforeach; ?>
</ul>
</div>
</section>
<section class="lt-preview-sample">
<div class="lt-preview-sample__inner">
<div class="lt-preview-sample__col">
<p class="lt-preview-sample__label">
<span class="lt-preview-sample__icon" aria-hidden="true">&#x2192;</span>
<?= htmlspecialchars($preview['scenario_label']) ?>
</p>
<div class="lt-preview-sample__input">
<p><?= $preview['scenario'] ?></p>
</div>
</div>
<div class="lt-preview-sample__col">
<p class="lt-preview-sample__label lt-preview-sample__label--out">
<span class="lt-preview-sample__icon" aria-hidden="true">&#x2713;</span>
<?= htmlspecialchars($preview['output_label']) ?>
</p>
<div class="lt-preview-sample__output lt-preview-output">
<pre><?= htmlspecialchars($preview['output']) ?></pre>
</div>
</div>
</div>
</section>
<section class="lt-preview-cta">
<div class="lt-preview-cta__inner">
<p class="lt-preview-cta__eyebrow">Do Better Norge members</p>
<h2 class="lt-preview-cta__title">Ready to use <?= htmlspecialchars($tool['label']) ?>?</h2>
<p class="lt-preview-cta__note">Free for Do Better Norge members. Documents are processed in memory and never stored.</p>
<a class="lt-preview-cta__btn" href="<?= htmlspecialchars($toolsLogin) ?>">Get access &rarr;</a>
<p class="lt-preview-cta__register">Not a member? <a href="https://dobetternorge.no/register.php">Join Do Better Norge</a></p>
</div>
<div class="lt-preview-cta__tools">
<p class="lt-preview-cta__tools-label">Other tools</p>
<div class="lt-preview-cta__tool-links">
<?php foreach ($tools as $otherSlug => $otherTool): if ($otherSlug === $slug) continue; ?>
<a href="preview.php?tool=<?= htmlspecialchars($otherSlug) ?>" class="lt-preview-cta__tool-link">
<?= htmlspecialchars($otherTool['label']) ?>
</a>
<?php endforeach; ?>
</div>
</div>
</section>
</main>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
</body>
</html>