diff --git a/extension/src/popup/components/form-header.ts b/extension/src/popup/components/form-header.ts
new file mode 100644
index 0000000..61e6f67
--- /dev/null
+++ b/extension/src/popup/components/form-header.ts
@@ -0,0 +1,20 @@
+/// Shared header chrome for typed form views (login, secure-note, identity, card,
+/// key, totp, document). Renders the title row plus a fullscreen-only "esc to
+/// cancel" subtitle. Use the existing `${...}` template-literal interpolation
+/// at call sites: `${renderFormHeader('new login')}`.
+///
+/// item-form.ts (the type-selection screen) uses a different header structure
+/// and does NOT consume this helper.
+
+import { isInTab } from '../../shared/state';
+
+export function renderFormHeader(titleText: string): string {
+ return `
+
+
${titleText}
+
+ ${isInTab() ? '' : ''}
+
+ ${isInTab() ? '
esc to cancel
' : ''}
+ `;
+}
diff --git a/extension/src/popup/components/types/card.ts b/extension/src/popup/components/types/card.ts
index 59c66ab..9f8d7c0 100644
--- a/extension/src/popup/components/types/card.ts
+++ b/extension/src/popup/components/types/card.ts
@@ -1,7 +1,8 @@
/// Card: number / holder / expiry MonthYear / cvv / pin / kind.
/// Detail view has a styled card-silhouette signature block.
-import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab, isInTab } from '../../../shared/state';
+import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab } from '../../../shared/state';
+import { renderFormHeader } from '../form-header';
import { REQUIRED_PILL_HTML } from '../../../shared/glyphs';
import type { Item, ItemId, ManifestEntry, CardKind, Section, AttachmentRef } from '../../../shared/types';
import {
@@ -174,12 +175,7 @@ export function renderForm(app: HTMLElement, mode: 'add' | 'edit', existing: Ite
app.innerHTML = `