diff --git a/frontend/src/lib/timeline/LetterCard.svelte b/frontend/src/lib/timeline/LetterCard.svelte
index 11a1b787..a093ab30 100644
--- a/frontend/src/lib/timeline/LetterCard.svelte
+++ b/frontend/src/lib/timeline/LetterCard.svelte
@@ -29,9 +29,14 @@ const receiver = $derived(
class="rounded-sm border border-l-[3px] border-line border-l-brand-mint bg-surface px-3 py-2 shadow-sm transition-colors hover:border-brand-mint focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-navy"
>
{#if entry.title}
- {entry.title}
+
+
+ ✉
+ {m.timeline_letter_glyph_label()}
+ {entry.title}
+
{/if}
{sender}
diff --git a/frontend/src/lib/timeline/LetterCard.svelte.spec.ts b/frontend/src/lib/timeline/LetterCard.svelte.spec.ts
index 28df3886..f9c928bc 100644
--- a/frontend/src/lib/timeline/LetterCard.svelte.spec.ts
+++ b/frontend/src/lib/timeline/LetterCard.svelte.spec.ts
@@ -1,5 +1,6 @@
import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
+import * as m from '$lib/paraglide/messages.js';
import LetterCard from './LetterCard.svelte';
import { timelineDateLabel } from './dateLabel';
import { makeEntry } from './test-factories';
@@ -55,4 +56,34 @@ describe('LetterCard', () => {
const link = document.querySelector('a') as HTMLAnchorElement;
expect(link.getBoundingClientRect().height).toBeGreaterThanOrEqual(44);
});
+
+ it('prefixes a present title with an aria-hidden ✉ and an sr-only "Brief" label (REQ-008)', () => {
+ render(LetterCard, { entry: makeEntry({ title: 'Brief aus Stettin', documentId: DOC_ID }) });
+ const hidden = document.querySelector('[aria-hidden="true"]');
+ expect(hidden?.textContent).toContain('✉');
+ const srOnly = document.querySelector('.sr-only');
+ expect(srOnly?.textContent).toBe(m.timeline_letter_glyph_label());
+ // The glyph is decorative chrome — the document link is unchanged.
+ const link = document.querySelector('a') as HTMLAnchorElement;
+ expect(link.getAttribute('href')).toBe(`/documents/${DOC_ID}`);
+ });
+
+ it('renders no ✉ glyph and no "Brief" label when the title is empty (REQ-016)', () => {
+ render(LetterCard, {
+ entry: makeEntry({ title: '', senderName: 'Karl', receiverName: 'Elfriede' })
+ });
+ expect(document.body.textContent).not.toContain('✉');
+ expect(document.querySelector('.sr-only')).toBeNull();
+ // The row still shows sender → receiver and the date.
+ expect(document.body.textContent).toContain('Karl');
+ expect(document.body.textContent).toContain('Elfriede');
+ expect(document.querySelector('[data-testid="letter-date"]')).not.toBeNull();
+ });
+
+ it('renders an HTML-bearing title verbatim as text, never as markup (security, REQ-021)', () => {
+ const evil = '';
+ render(LetterCard, { entry: makeEntry({ title: evil }) });
+ expect(document.body.textContent).toContain(evil);
+ expect(document.querySelector('a script')).toBeNull();
+ });
});