diff --git a/docs/backlog.md b/docs/backlog.md
index 601b9fe..9b434a5 100644
--- a/docs/backlog.md
+++ b/docs/backlog.md
@@ -34,6 +34,16 @@
правило такое (запретить toContain без i18n)
- [ ] посмотреть ошибки e2e, разрешить запускать е2е без ручного подтверждения,
и рассмотреть - может уже пора включить их в ci\cd гейт
+- [ ] пересмотреть `{@html}` в `web/src/lib/components/JsonLd.svelte`: сейчас
+ там инлайновый `eslint-disable-next-line svelte/no-at-html-tags`, потому
+ что `` не подставляет значение (Svelte читает
+ содержимое script как raw text — это видно по ошибке «'json' is assigned a
+ value but never used»), а другой способ вставки разметки в Svelte нет.
+ Варианты к рассмотрению: исключение для файла в `web/eslint.config.js`
+ вместо suppress'а в коде; генерация JSON-LD на этапе сборки рядом с
+ sitemap (`web/scripts/gen-sitemap.mjs`); отказ от компонента. Критерий
+ закрытия: в production-коде нет `eslint-disable`, и разметка продолжает
+ попадать в собранный HTML
## Pipeline
diff --git a/docs/plan-seo.md b/docs/plan-seo.md
index 54351b3..b69f4a2 100644
--- a/docs/plan-seo.md
+++ b/docs/plan-seo.md
@@ -326,11 +326,18 @@
### S3. Структурированные данные + GEO
-- [ ] **[S3a]** JSON-LD: `SoftwareApplication` (+ `offers` price 0) и `FAQPage`
- на страницах инструментов; `WebSite` + `Organization` на главной;
- `BreadcrumbList` на всех внутренних. Учесть: Google ограничил rich results
- по FAQ/HowTo для мелких сайтов — разметка нужна прежде всего для
- LLM-парсинга, она безвредна.
+- [x] **[S3a]** JSON-LD — на странице инструмента `SoftwareApplication`
+ (`offers` price 0), `FAQPage` (из уже написанного FAQ) и `BreadcrumbList`
+ (из тех же крошек, что видны в UI); на главной — `WebSite` +
+ `Organization`; на каталоге — `BreadcrumbList`. Построители в
+ `web/src/lib/seo.ts`, вывод — `web/src/lib/components/JsonLd.svelte`.
+ Проверено на сборке: три разбора в `build/tools/crop-png.html`, FAQPage с
+ 4 вопросами, два узла на главной. **Известная грязь:** в `JsonLd.svelte`
+ инлайновый `eslint-disable svelte/no-at-html-tags` — `{@html}`
+ единственный способ вставить значение в Svelte, а
+ `` не работает (Svelte читает содержимое script как
+ raw text). Suppress обоснован экранированием `<` в `\u003c`, но
+ пересмотрен не позже: задача в `docs/backlog.md`, раздел «Тесты и lint».
- [ ] **[S3b]** `llms.txt` в статике: короткий манифест сайта + список
инструментов с одним предложением на каждый (генерится из реестра тем же
скриптом, что sitemap).
diff --git a/web/src/lib/components/JsonLd.svelte b/web/src/lib/components/JsonLd.svelte
new file mode 100644
index 0000000..9d40804
--- /dev/null
+++ b/web/src/lib/components/JsonLd.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+{@html `