diff --git a/web/public/favicon.svg b/web/public/favicon.svg
index 5214238..6b6c70f 100644
--- a/web/public/favicon.svg
+++ b/web/public/favicon.svg
@@ -1,4 +1 @@
-
+
diff --git a/web/src/assets/marks/aircards-symbol.svg b/web/src/assets/marks/aircards-symbol.svg
new file mode 100644
index 0000000..bad8629
--- /dev/null
+++ b/web/src/assets/marks/aircards-symbol.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/aircards-text.svg b/web/src/assets/marks/aircards-text.svg
new file mode 100644
index 0000000..85b8427
--- /dev/null
+++ b/web/src/assets/marks/aircards-text.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/amex-blue.svg b/web/src/assets/marks/amex-blue.svg
new file mode 100644
index 0000000..ccb37e6
--- /dev/null
+++ b/web/src/assets/marks/amex-blue.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/amex-outline.svg b/web/src/assets/marks/amex-outline.svg
new file mode 100644
index 0000000..236dd40
--- /dev/null
+++ b/web/src/assets/marks/amex-outline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/amex-select.svg b/web/src/assets/marks/amex-select.svg
new file mode 100644
index 0000000..ccb37e6
--- /dev/null
+++ b/web/src/assets/marks/amex-select.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/apple.svg b/web/src/assets/marks/apple.svg
new file mode 100644
index 0000000..31b057a
--- /dev/null
+++ b/web/src/assets/marks/apple.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/discover.svg b/web/src/assets/marks/discover.svg
new file mode 100644
index 0000000..d55863a
--- /dev/null
+++ b/web/src/assets/marks/discover.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/jcb.svg b/web/src/assets/marks/jcb.svg
new file mode 100644
index 0000000..133310f
--- /dev/null
+++ b/web/src/assets/marks/jcb.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/mc-classic.svg b/web/src/assets/marks/mc-classic.svg
new file mode 100644
index 0000000..37a8330
--- /dev/null
+++ b/web/src/assets/marks/mc-classic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/mc-color.svg b/web/src/assets/marks/mc-color.svg
new file mode 100644
index 0000000..1aef221
--- /dev/null
+++ b/web/src/assets/marks/mc-color.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/mc-silver-text.svg b/web/src/assets/marks/mc-silver-text.svg
new file mode 100644
index 0000000..4aa4250
--- /dev/null
+++ b/web/src/assets/marks/mc-silver-text.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/mc-silver.svg b/web/src/assets/marks/mc-silver.svg
new file mode 100644
index 0000000..fb19c12
--- /dev/null
+++ b/web/src/assets/marks/mc-silver.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/mc-wordmark.svg b/web/src/assets/marks/mc-wordmark.svg
new file mode 100644
index 0000000..c24ddcc
--- /dev/null
+++ b/web/src/assets/marks/mc-wordmark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/up-color.svg b/web/src/assets/marks/up-color.svg
new file mode 100644
index 0000000..8d51b91
--- /dev/null
+++ b/web/src/assets/marks/up-color.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/up-english.svg b/web/src/assets/marks/up-english.svg
new file mode 100644
index 0000000..8c92134
--- /dev/null
+++ b/web/src/assets/marks/up-english.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/up-silver.svg b/web/src/assets/marks/up-silver.svg
new file mode 100644
index 0000000..9d9dd36
--- /dev/null
+++ b/web/src/assets/marks/up-silver.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/up-white-frame.svg b/web/src/assets/marks/up-white-frame.svg
new file mode 100644
index 0000000..4660669
--- /dev/null
+++ b/web/src/assets/marks/up-white-frame.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/visa-auto.svg b/web/src/assets/marks/visa-auto.svg
new file mode 100644
index 0000000..1f64047
--- /dev/null
+++ b/web/src/assets/marks/visa-auto.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/visa-blue.svg b/web/src/assets/marks/visa-blue.svg
new file mode 100644
index 0000000..9fe1811
--- /dev/null
+++ b/web/src/assets/marks/visa-blue.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/visa-navy.svg b/web/src/assets/marks/visa-navy.svg
new file mode 100644
index 0000000..739c9bb
--- /dev/null
+++ b/web/src/assets/marks/visa-navy.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/assets/marks/visa-silver.svg b/web/src/assets/marks/visa-silver.svg
new file mode 100644
index 0000000..673abb0
--- /dev/null
+++ b/web/src/assets/marks/visa-silver.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/card/marks.test.ts b/web/src/card/marks.test.ts
new file mode 100644
index 0000000..651af51
--- /dev/null
+++ b/web/src/card/marks.test.ts
@@ -0,0 +1,165 @@
+import { describe, it, expect } from 'vitest';
+import { NETWORK_IDS } from './config';
+import favicon from '../../public/favicon.svg?raw';
+import {
+ BRAND_MARKS,
+ NETWORK_MARKS,
+ getBrandMark,
+ getNetworkMark,
+ loadMarkImage,
+ svgAspect,
+ tintSvg,
+} from './marks';
+
+const NETWORK_LABELS = [
+ 'Automatisch', 'Blau', 'Marineblau', 'Metallisches Silber',
+ 'Farbe', 'Metallisches Silber', 'Wortmarke', 'Silber + Text', 'Klassisch',
+ 'Farbe', 'Metallisches Silber', 'Weißer Rahmen', 'Englisch',
+ 'Blau', 'Umriss', 'Select',
+ 'JCB', 'Discover',
+];
+
+const ALL = [...NETWORK_MARKS, ...BRAND_MARKS];
+const TINT_IDS = ['visa-auto', 'amex-outline', 'aircards-symbol', 'aircards-text', 'apple'];
+
+describe('Registers', () => {
+ it('NETWORK_MARKS folgt NETWORK_IDS', () => {
+ expect(NETWORK_MARKS.map((m) => m.id)).toEqual([...NETWORK_IDS]);
+ });
+
+ it('Labels stehen im Wortlaut der Vorlage in Reihenfolge', () => {
+ expect(NETWORK_MARKS.map((m) => m.label)).toEqual(NETWORK_LABELS);
+ expect(BRAND_MARKS.map((m) => m.label)).toEqual(['AirCards · Symbol', 'AirCards · Text', 'Apple']);
+ });
+
+ it('BRAND_MARKS: Logo-Gruppe, feste Reihenfolge', () => {
+ expect(BRAND_MARKS.map((m) => m.id)).toEqual(['aircards-symbol', 'aircards-text', 'apple']);
+ expect(BRAND_MARKS.every((m) => m.group === 'Logo')).toBe(true);
+ });
+
+ it('Gruppen der Netzwerke', () => {
+ const groups = NETWORK_MARKS.map((m) => m.group);
+ expect(groups).toEqual([
+ 'Visa', 'Visa', 'Visa', 'Visa',
+ 'Mastercard', 'Mastercard', 'Mastercard', 'Mastercard', 'Mastercard',
+ 'UnionPay', 'UnionPay', 'UnionPay', 'UnionPay',
+ 'American Express', 'American Express', 'American Express',
+ 'JCB', 'Discover',
+ ]);
+ });
+
+ it('getNetworkMark / getBrandMark finden den Eintrag', () => {
+ for (const id of NETWORK_IDS) expect(getNetworkMark(id).id).toBe(id);
+ expect(getBrandMark('apple').id).toBe('apple');
+ expect(getBrandMark('aircards-text').label).toBe('AirCards · Text');
+ });
+});
+
+describe('SVG-Inhalt', () => {
+ it.each(ALL.map((m) => [m.id, m] as const))('%s ist ein sauberes SVG', (_id, m) => {
+ expect(m.svg.trimStart().startsWith('