This commit is contained in:
2026-07-20 21:03:05 +02:00
parent dc60551e07
commit c3c85eef21
70 changed files with 8049 additions and 1 deletions
+8
View File
@@ -0,0 +1,8 @@
package web
import "embed"
// Files contains the self-contained web UI and public schemas.
//
//go:embed templates/*.html static/*
var Files embed.FS
+126
View File
@@ -0,0 +1,126 @@
(() => {
const cfg = window.APP_CONFIG;
if (!cfg) return;
const base = cfg.baseURL.replace(/\/$/, '');
const form = document.getElementById('generator-form');
if (!form) return;
const $ = id => document.getElementById(id);
const presets = {
'no-ai': {extent:'none', activities:'', review:'editorial'},
research: {extent:'assisted', activities:'research', review:'editorial'},
summary: {extent:'assisted', activities:'summarisation', review:'editorial'},
full: {extent:'full', activities:'generation', review:'editorial'}
};
function value(id) {
const el = $(id);
return el && !el.disabled ? el.value.trim() : '';
}
function setArticleParams(params) {
params.set('mode', 'article');
const fields = {
text: 'article-text',
coverImage: 'article-cover-image',
image: 'article-image',
research: 'article-research',
translation: 'article-translation',
code: 'article-code'
};
Object.entries(fields).forEach(([name, id]) => {
params.set(name + 'Extent', $(id).value);
params.set(name + 'Review', $(`${id}-review`).value);
});
}
function update(applyPreset = false) {
const mode = $('declaration-mode').value;
const articleMode = mode === 'article';
$('single-fields').hidden = articleMode;
$('article-fields').hidden = !articleMode;
const preset = $('preset').value;
if (!articleMode && applyPreset && presets[preset]) {
$('extent').value = presets[preset].extent;
$('activities').value = presets[preset].activities;
$('review').value = presets[preset].review;
}
const lang = $('lang').value;
const locale = cfg.locales[lang] || cfg.locales.en;
const assurance = $('assurance').value;
const assuranceDescription = locale.text[`assurance_${assurance}_description`];
if ($('assurance-description') && assuranceDescription) {
$('assurance-description').textContent = assuranceDescription;
}
const params = new URLSearchParams();
if (articleMode) {
setArticleParams(params);
} else {
if (preset) params.set('preset', preset);
params.set('component', $('component').value);
params.set('extent', $('extent').value);
if ($('activities').value.trim()) params.set('activities', $('activities').value.trim());
params.set('review', $('review').value);
}
params.set('lang', lang);
params.set('assurance', assurance);
if ($('subject').value.trim()) params.set('subject', $('subject').value.trim());
if (cfg.capabilities.custom_text) {
if (value('custom-title')) params.set('customTitle', value('custom-title'));
if (value('custom-description')) params.set('customDescription', value('custom-description'));
}
if (cfg.capabilities.custom_badge) {
if (value('badge-label')) params.set('badgeLabel', value('badge-label'));
if (value('badge-message')) params.set('badgeMessage', value('badge-message'));
if (value('left-color')) params.set('leftColor', value('left-color'));
if (value('right-color')) params.set('rightColor', value('right-color'));
}
const declarationURL = `${base}/declaration?${params}`;
const badgeParams = new URLSearchParams(params);
badgeParams.set('theme', $('theme').value);
badgeParams.set('link', declarationURL);
const badgeURL = `${base}/v1/badge.svg?${badgeParams}`;
const manifestURL = `${base}/v1/declaration.json?${params}`;
const alt = articleMode ? ((cfg.locales[lang] || cfg.locales.en).text.badge_article || 'Article transparency') : 'AI usage disclosure';
$('preview-badge').src = badgeURL;
$('preview-badge').alt = alt;
$('preview-link').href = declarationURL;
$('html-code').value = `<a href="${declarationURL}"><img src="${badgeURL}" alt="${alt}"></a>`;
$('markdown-code').value = `[![${alt}](${badgeURL})](${declarationURL})`;
$('json-code').value = manifestURL;
}
document.querySelectorAll('#article-fields select[data-review]').forEach(extentSelect => {
extentSelect.addEventListener('change', () => {
const reviewSelect = $(extentSelect.dataset.review);
if (!reviewSelect) return;
if (extentSelect.value === 'none') {
reviewSelect.value = 'none';
} else if (reviewSelect.value === 'none') {
reviewSelect.value = 'editorial';
}
update(false);
});
});
form.addEventListener('input', event => update(event.target.id === 'preset'));
form.addEventListener('change', event => update(event.target.id === 'preset'));
document.querySelector('.pro-panel')?.addEventListener('input', () => update(false));
$('copy-html')?.addEventListener('click', async () => {
await navigator.clipboard.writeText($('html-code').value);
const locale = cfg.locales[$('lang').value] || cfg.locales.en;
$('copy-html').textContent = locale.text.copied || 'Copied';
setTimeout(() => { $('copy-html').textContent = locale.text.copy_html || 'Copy HTML'; }, 1600);
});
$('page-language')?.addEventListener('change', event => {
const url = new URL(window.location.href);
url.searchParams.set('lang', event.target.value);
window.location.href = url.toString();
});
update(true);
})();
+22
View File
@@ -0,0 +1,22 @@
(() => {
document.getElementById('marketing-language')?.addEventListener('change', event => {
const next = new URL(window.location.href);
next.searchParams.set('lang', event.target.value);
window.location.assign(next.toString());
});
document.querySelectorAll('.copy-code').forEach(button => {
button.addEventListener('click', async () => {
const target = document.getElementById(button.dataset.copyTarget);
if (!target) return;
const original = button.dataset.label || button.textContent;
try {
await navigator.clipboard.writeText(target.textContent);
button.textContent = button.dataset.copied || original;
window.setTimeout(() => { button.textContent = original; }, 1200);
} catch (_) {
button.textContent = original;
}
});
});
})();
File diff suppressed because one or more lines are too long
+60
View File
@@ -0,0 +1,60 @@
{{define "declaration.html"}}
<!doctype html>
<html lang="{{.Lang}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{{.Title}} · {{.Name}}</title>
<meta name="description" content="{{.Description}}">
<link rel="canonical" href="{{.CanonicalURL}}">
<link rel="describedby" type="application/ld+json" href="{{.ManifestURL}}">
{{range .LanguageLinks}}<link rel="alternate" hreflang="{{.Code}}" href="{{.AbsoluteURL}}">{{end}}
<link rel="alternate" hreflang="x-default" href="{{.DefaultLanguageURL}}">
<link rel="stylesheet" href="/static/style.css">
<script type="application/ld+json">{{.JSONLD}}</script>
</head>
<body>
<header class="site-header"><a class="brand" href="/?lang={{.Lang}}">{{.Name}}</a><nav><a href="/?lang={{.Lang}}">{{index .Text "nav_generator"}}</a><a href="{{.ManifestURL}}">JSON-LD</a></nav></header>
<main class="declaration-page">
<a class="back" href="/?lang={{.Lang}}">← {{index .Text "back"}}</a>
<article class="declaration-card">
<div class="declaration-toolbar">
<p class="eyebrow">{{index .Text "declaration_eyebrow"}} · Schema {{.Declaration.SchemaVersion}}</p>
<form class="declaration-language" method="get" action="/declaration">
<label for="declaration-language">{{index .Text "field_language"}}</label>
<select id="declaration-language" aria-label="{{index .Text "field_language"}}" onchange="window.location.assign(this.value)">
{{range .LanguageLinks}}<option value="{{.URL}}" lang="{{.Code}}"{{if .Current}} selected{{end}}>{{.Name}}</option>{{end}}
</select>
<noscript><div class="language-link-list">{{range .LanguageLinks}}<a href="{{.URL}}" lang="{{.Code}}"{{if .Current}} aria-current="page"{{end}}>{{.Name}}</a>{{end}}</div></noscript>
</form>
</div>
<h1>{{.Title}}</h1>
<p class="lead">{{.Description}}</p>
<p class="declaration-badge"><img src="{{.BadgeURL}}" alt="{{.Title}}"></p>
<section class="summary-section" aria-labelledby="summary-title">
<h2 id="summary-title">{{index .Text "article_summary_heading"}}</h2>
<div class="summary-copy">{{range .Summary}}<p>{{.}}</p>{{end}}</div>
</section>
<section class="component-section" aria-labelledby="component-title">
<h2 id="component-title">{{index .Text "article_table_heading"}}</h2>
<div class="component-table-wrap">
<table class="component-table">
<colgroup><col class="component-col"><col class="extent-col"><col class="activity-col"><col class="review-col"></colgroup>
<thead><tr><th scope="col">{{index .Text "table_component"}}</th><th scope="col">{{index .Text "table_extent"}}</th><th scope="col">{{index .Text "table_activities"}}</th><th scope="col">{{index .Text "table_review"}}</th></tr></thead>
<tbody>{{range .ComponentRows}}<tr><th scope="row">{{.Name}}</th><td>{{.Extent}}</td><td>{{.Activities}}</td><td>{{.Review}}</td></tr>{{if .Note}}<tr class="component-note"><td colspan="4">{{.Note}}</td></tr>{{end}}{{end}}</tbody>
</table>
</div>
</section>
<dl class="facts metadata-facts">
{{range .Facts}}<div><dt>{{.Label}}</dt><dd>{{if .Link}}<a href="{{.Link}}">{{.Value}}</a>{{else}}{{.Value}}{{end}}</dd></div>{{end}}
</dl>
<div class="notice"><strong>{{index .Text "transparency_label"}}</strong> {{index .Text "transparency_text"}}</div>
</article>
</main>
<footer><span><a href="{{.ManifestURL}}">{{index .Text "manifest"}}</a></span><span><a href="{{.ContactURL}}">{{index .Text "nav_background"}}</a></span></footer>
</body>
</html>
{{end}}
+140
View File
@@ -0,0 +1,140 @@
{{define "index.html"}}
<!doctype html>
<html lang="{{.Lang}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{{.Name}}</title>
<meta name="description" content="{{index .Text "meta_description"}}">
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<header class="site-header">
<a class="brand" href="/?lang={{.Lang}}">{{.Name}}</a>
<nav><a href="/product?lang={{.Lang}}">{{index .Text "nav_product"}}</a><a href="#generator">{{index .Text "nav_generator"}}</a><a href="#api">{{index .Text "nav_api"}}</a><a href="{{.ContactURL}}">{{index .Text "nav_background"}}</a></nav>
<label class="language-switch"><span class="sr-only">{{index .Text "field_language"}}</span><select id="page-language">
{{range .Languages}}<option value="{{.Code}}" {{if eq $.Lang .Code}}selected{{end}}>{{.Name}}</option>{{end}}
</select></label>
</header>
<main>
<section class="hero">
<p class="eyebrow">{{index .Text "standard_eyebrow"}}</p>
<h1>{{index .Text "hero_title"}}</h1>
<p class="lead">{{index .Text "hero_lead"}}</p>
<div class="badge-row">
{{range .Presets}}<a href="/declaration?preset={{.Value}}&amp;lang={{$.Lang}}"><img src="/badge/{{.Value}}.svg?theme=mono&amp;link=auto&amp;lang={{$.Lang}}" alt="{{.Label}}"></a>{{end}}
</div>
</section>
<section id="generator" class="panel">
<div>
<p class="eyebrow">{{index .Text "generator_eyebrow"}}</p>
<h2>{{index .Text "generator_title"}}</h2>
<p>{{index .Text "generator_intro"}}</p>
</div>
<form id="generator-form" class="generator-grid">
<label class="wide">{{index .Text "field_mode"}}
<select id="declaration-mode" name="mode"><option value="single">{{index .Text "mode_single"}}</option><option value="article">{{index .Text "mode_article"}}</option></select>
</label>
<div id="single-fields" class="form-subgrid wide">
<label>{{index .Text "field_preset"}}
<select id="preset" name="preset">
{{range .Presets}}<option value="{{.Value}}">{{.Label}}</option>{{end}}
<option value="">{{index .Text "option_custom"}}</option>
</select>
</label>
<label>{{index .Text "field_component"}}
<select id="component" name="component">{{range .Components}}<option value="{{.Value}}">{{.Label}}</option>{{end}}</select>
</label>
<label>{{index .Text "field_extent"}}
<select id="extent" name="extent">{{range .Extents}}<option value="{{.Value}}">{{.Label}}</option>{{end}}</select>
</label>
<label>{{index .Text "field_review"}}
<select id="review" name="review">{{range .Reviews}}<option value="{{.Value}}">{{.Label}}</option>{{end}}</select>
</label>
<label class="wide">{{index .Text "field_activities"}}
<input id="activities" name="activities" value="research" placeholder="research,summarisation">
<small>{{index .Text "activities_help"}}</small>
</label>
</div>
<fieldset id="article-fields" class="article-builder wide" hidden>
<legend>{{index .Text "article_fields"}}</legend>
<p>{{index .Text "article_help"}}</p>
<div class="article-component-grid">
<div class="article-component"><label>Text<select id="article-text" data-review="article-text-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-text-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
<div class="article-component"><label>{{index .Text "fact_activities"}} / Recherche<select id="article-research" data-review="article-research-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "assisted"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-research-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "editorial"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
<div class="article-component"><label>Cover / {{index .Text "fact_component"}}<select id="article-cover-image" data-review="article-cover-image-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-cover-image-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
<div class="article-component"><label>Images<select id="article-image" data-review="article-image-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "full"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-image-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "editorial"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
<div class="article-component"><label>Translation<select id="article-translation" data-review="article-translation-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-translation-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
<div class="article-component"><label>Code<select id="article-code" data-review="article-code-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-code-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
</div>
</fieldset>
<label>{{index .Text "field_language"}}
<select id="lang" name="lang">{{range .Languages}}<option value="{{.Code}}" {{if eq $.Lang .Code}}selected{{end}}>{{.Name}}</option>{{end}}</select>
</label>
<label>{{index .Text "field_theme"}}
<select id="theme" name="theme"><option value="mono">{{index .Text "theme_mono"}}</option><option value="color">{{index .Text "theme_color"}}</option></select>
</label>
<label class="wide">{{index .Text "field_assurance"}}
<select id="assurance" name="assurance">{{range .Assurances}}<option value="{{.Value}}">{{.Label}}</option>{{end}}</select>
<small id="assurance-description">{{index .Text "assurance_selfDeclared_description"}}</small>
</label>
<label class="wide">{{index .Text "field_subject"}}
<input id="subject" name="subject" type="url" placeholder="https://example.org/article">
</label>
</form>
{{if or .CustomText .CustomBadge}}
<section class="pro-panel enabled" aria-labelledby="pro-title">
<div class="pro-heading">
<div><p class="eyebrow">{{index .Text "pro_eyebrow"}}</p><h2 id="pro-title">{{index .Text "pro_title"}}</h2></div>
<span class="edition-pill">{{.License.Edition}}</span>
</div>
<p>{{index .Text "pro_enabled"}}</p>
<div class="generator-grid compact">
<label>{{index .Text "custom_title"}}
<input id="custom-title" maxlength="120" {{if not .CustomText}}disabled placeholder="{{index .Text "pro_required"}}"{{end}}>
</label>
<label>{{index .Text "custom_description"}}
<input id="custom-description" maxlength="500" {{if not .CustomText}}disabled placeholder="{{index .Text "pro_required"}}"{{end}}>
</label>
<label>{{index .Text "custom_badge_label"}}
<input id="badge-label" maxlength="40" {{if not .CustomBadge}}disabled placeholder="{{index .Text "pro_required"}}"{{end}}>
</label>
<label>{{index .Text "custom_badge_message"}}
<input id="badge-message" maxlength="80" {{if not .CustomBadge}}disabled placeholder="{{index .Text "pro_required"}}"{{end}}>
</label>
<label>{{index .Text "custom_left_color"}}
<input id="left-color" maxlength="7" pattern="#[0-9A-Fa-f]{6}" placeholder="#262626" {{if not .CustomBadge}}disabled{{end}}>
</label>
<label>{{index .Text "custom_right_color"}}
<input id="right-color" maxlength="7" pattern="#[0-9A-Fa-f]{6}" placeholder="#2b6cb0" {{if not .CustomBadge}}disabled{{end}}>
</label>
</div>
</section>
{{end}}
<div class="preview-card">
<p class="eyebrow">{{index .Text "preview"}}</p>
<a id="preview-link" href="#"><img id="preview-badge" alt="{{index .Text "preview"}}"></a>
<div class="code-tabs">
<label>HTML<textarea id="html-code" readonly></textarea></label>
<label>Markdown<textarea id="markdown-code" readonly></textarea></label>
<label>JSON-LD URL<textarea id="json-code" readonly></textarea></label>
</div>
<button id="copy-html" type="button">{{index .Text "copy_html"}}</button>
</div>
</section>
<section id="api" class="docs-grid">
<article><p class="eyebrow">SVG</p><h2>{{index .Text "api_badge_title"}}</h2><code>{{.BaseURL}}/badge/research.svg?lang={{.Lang}}</code><p>{{index .Text "api_badge_desc"}}</p></article>
<article><p class="eyebrow">JSON-LD</p><h2>{{index .Text "api_manifest_title"}}</h2><code>{{.BaseURL}}/v1/declaration.json?preset=research&amp;lang={{.Lang}}</code><p>{{index .Text "api_manifest_desc"}}</p></article>
<article><p class="eyebrow">HA</p><h2>{{index .Text "api_stateless_title"}}</h2><p>{{index .Text "api_stateless_desc"}}</p></article>
</section>
</main>
<footer><span>AI Usage Disclosure · App 1.6.1 · Schema 1.1 · {{.License.Edition}}</span><span>{{index .Text "footer_no_legal"}}</span></footer>
<script>window.APP_CONFIG={{.AppConfig}};</script>
<script src="/static/app.js" defer></script>
</body>
</html>
{{end}}
+126
View File
@@ -0,0 +1,126 @@
{{define "marketing.html"}}
<!doctype html>
<html lang="{{.Lang}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{{.Name}} · {{.Marketing.NavFeatures}}</title>
<meta name="description" content="{{.Marketing.MetaDescription}}">
<link rel="canonical" href="{{.BaseURL}}/product?lang={{.Lang}}">
<link rel="stylesheet" href="/static/style.css">
</head>
<body class="marketing-page">
<header class="site-header marketing-header">
<a class="brand" href="/product?lang={{.Lang}}">{{.Name}}</a>
<nav>
<a href="#features">{{.Marketing.NavFeatures}}</a>
<a href="#install">{{.Marketing.NavInstall}}</a>
<a href="/?lang={{.Lang}}#generator">{{.Marketing.NavGenerator}}</a>
<a href="{{.ContactURL}}">{{.Marketing.NavBackground}}</a>
</nav>
<label class="language-switch"><span class="sr-only">{{.Marketing.LanguageLabel}}</span><select id="marketing-language">
{{range .Languages}}<option value="{{.Code}}" {{if eq $.Lang .Code}}selected{{end}}>{{.Name}}</option>{{end}}
</select></label>
</header>
<main class="marketing-main">
<section class="marketing-hero">
<div class="marketing-hero-copy">
<p class="eyebrow">{{.Marketing.HeroEyebrow}}</p>
<h1>{{.Marketing.HeroTitle}}</h1>
<p class="lead">{{.Marketing.HeroLead}}</p>
<div class="cta-row">
<a class="button primary" href="/?lang={{.Lang}}#generator">{{.Marketing.PrimaryCTA}}</a>
</div>
<ul class="proof-list" aria-label="Product highlights">
{{range .Marketing.Proof}}<li>{{.}}</li>{{end}}
</ul>
</div>
<div class="product-demo" aria-label="Badge examples">
<div class="demo-window">
<div class="demo-bar"><span></span><span></span><span></span></div>
<div class="demo-content">
<p class="demo-url">{{.BaseURL}}/badge/research.svg</p>
<img src="/badge/research.svg?lang={{.Lang}}&amp;theme=color" alt="Research badge">
<img src="/badge/summary.svg?lang={{.Lang}}&amp;theme=mono" alt="Summary badge">
<img src="/badge/full.svg?lang={{.Lang}}&amp;theme=color" alt="Full generation badge">
<pre><code>{
"@type": "AIUsageDeclaration",
"schemaVersion": "1.1",
"assurance": "selfDeclared"
}</code></pre>
</div>
</div>
</div>
</section>
<section id="features" class="marketing-section">
<div class="section-heading">
<p class="eyebrow">{{.Marketing.FeaturesEyebrow}}</p>
<h2>{{.Marketing.FeaturesTitle}}</h2>
<p>{{.Marketing.FeaturesLead}}</p>
</div>
<div class="feature-grid">
{{range .Marketing.Features}}
<article class="feature-card">
<p class="feature-kicker">{{.Kicker}}</p>
<h3>{{.Title}}</h3>
<p>{{.Description}}</p>
</article>
{{end}}
</div>
</section>
<section id="install" class="marketing-section install-section">
<div class="section-heading">
<p class="eyebrow">{{.Marketing.InstallEyebrow}}</p>
<h2>{{.Marketing.InstallTitle}}</h2>
<p>{{.Marketing.InstallLead}}</p>
</div>
<div class="install-list">
{{range $index, $method := .Marketing.InstallMethods}}
<details class="install-method" {{if eq $index 0}}open{{end}}>
<summary><span><strong>{{$method.Title}}</strong><small>{{$method.Summary}}</small></span><span aria-hidden="true">+</span></summary>
<div class="code-block">
<button type="button" class="copy-code" data-copy-target="install-{{$method.ID}}" data-label="{{$.Marketing.CopyLabel}}" data-copied="{{$.Marketing.CopiedLabel}}">{{$.Marketing.CopyLabel}}</button>
<pre id="install-{{$method.ID}}"><code>{{$method.Code}}</code></pre>
</div>
</details>
{{end}}
</div>
<div class="config-panel">
<h3>{{.Marketing.ConfigTitle}}</h3>
<div class="comparison-wrap">
<table class="config-table">
<thead><tr><th>{{.Marketing.ConfigVariable}}</th><th>{{.Marketing.ConfigDefault}}</th><th>{{.Marketing.ConfigMeaning}}</th></tr></thead>
<tbody>{{range .Marketing.Config}}<tr><td><code>{{.Name}}</code></td><td><code>{{.Default}}</code></td><td>{{.Description}}</td></tr>{{end}}</tbody>
</table>
</div>
</div>
</section>
<section class="marketing-section faq-section">
<div class="section-heading">
<p class="eyebrow">{{.Marketing.FAQEyebrow}}</p>
<h2>{{.Marketing.FAQTitle}}</h2>
</div>
<div class="faq-grid">
{{range .Marketing.FAQs}}<details><summary>{{.Question}}</summary><p>{{.Answer}}</p></details>{{end}}
</div>
</section>
<section class="final-cta">
<h2>{{.Marketing.FinalTitle}}</h2>
<p>{{.Marketing.FinalLead}}</p>
<div class="cta-row">
<a class="button primary" href="/?lang={{.Lang}}#generator">{{.Marketing.FinalPrimary}}</a>
</div>
</section>
</main>
<footer><span>{{.Marketing.Footer}}</span><span>App 1.6 · Schema 1.1 · {{.License.Edition}}</span></footer>
<script src="/static/marketing.js" defer></script>
</body>
</html>
{{end}}