diff --git a/README.md b/README.md
index 86ae5f1..46b6f34 100644
--- a/README.md
+++ b/README.md
@@ -461,7 +461,7 @@ secure-messenger/
app/windows/ CMake, runner, and bitdm.iss for the installer
app/linux/ runner; the release tar.gz comes from .github/workflows/linux.yml
app/web/ shell for bitdm.net/app, built as in deploy/webapp/README.md
- app/tool/symbol/ 3 Python scripts that render and distribute the app icon
+ app/tool/symbol/ the BitDM mark (f1.py) and the script that writes every icon
app/tool/*.py checks for the main.dart split: pure move, name collisions
server/ Python relay + blob store, 5,106 lines; tests 5,568 lines,
206 tests (18.10.2026)
diff --git a/secure-messenger/DESIGN.md b/secure-messenger/DESIGN.md
index a454459..3d65f7f 100644
--- a/secure-messenger/DESIGN.md
+++ b/secure-messenger/DESIGN.md
@@ -59,6 +59,14 @@ Plattenbeschriftungen, 0.12em für Modulbezeichnungen, 0.08em für Zustände.
- Tiefe entsteht durch **Versatz und weiche Streuung**, nie durch farbige Höfe
ohne Versatz.
+## Zeichen
+
+Seit 24.10.2026: ein **Anzeigefenster** mit Punktmatrix, darin leuchtet **DM**
+im Akzent, die übrigen Punkte sind aus. Es ist dieselbe Welt wie die App: eine
+Gerätefront, die nur dort leuchtet, wo sie etwas anzeigt. Bis 32 Pixel gibt es
+eine Kleinfassung ohne Fenster und Raster. Vorlage, Maße und alle Ausgaben:
+`app/tool/symbol/` (`py tool/symbol/verteilen.py`).
+
## Grammatik der Frontplatte
Neue Flächen erben diese Sprache:
diff --git a/secure-messenger/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/secure-messenger/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
index 6deb4dd..f6aed61 100644
Binary files a/secure-messenger/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/secure-messenger/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/secure-messenger/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/secure-messenger/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
index 612e90b..81874cc 100644
Binary files a/secure-messenger/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/secure-messenger/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/secure-messenger/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/secure-messenger/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index a7acfd0..63a7e50 100644
Binary files a/secure-messenger/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/secure-messenger/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/secure-messenger/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/secure-messenger/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index b173a8b..d4213f9 100644
Binary files a/secure-messenger/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/secure-messenger/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/secure-messenger/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/secure-messenger/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index f03a040..7acaf81 100644
Binary files a/secure-messenger/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/secure-messenger/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/secure-messenger/app/fastlane/metadata/android/en-US/images/icon.png b/secure-messenger/app/fastlane/metadata/android/en-US/images/icon.png
index df242e9..b9b3e90 100644
Binary files a/secure-messenger/app/fastlane/metadata/android/en-US/images/icon.png and b/secure-messenger/app/fastlane/metadata/android/en-US/images/icon.png differ
diff --git a/secure-messenger/app/icon_bitdm_1024.png b/secure-messenger/app/icon_bitdm_1024.png
deleted file mode 100644
index 4726e60..0000000
Binary files a/secure-messenger/app/icon_bitdm_1024.png and /dev/null differ
diff --git a/secure-messenger/app/icon_bitdm_klein.png b/secure-messenger/app/icon_bitdm_klein.png
deleted file mode 100644
index 0136d27..0000000
Binary files a/secure-messenger/app/icon_bitdm_klein.png and /dev/null differ
diff --git a/secure-messenger/app/icon_master_1024.png b/secure-messenger/app/icon_master_1024.png
deleted file mode 100644
index 700d539..0000000
Binary files a/secure-messenger/app/icon_master_1024.png and /dev/null differ
diff --git a/secure-messenger/app/tool/symbol/01_gross.py b/secure-messenger/app/tool/symbol/01_gross.py
deleted file mode 100644
index 84cf707..0000000
--- a/secure-messenger/app/tool/symbol/01_gross.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# -*- coding: utf-8 -*-
-"""BitDM-Symbol: Sprechblase als Umriss, B in Doto.
-
-DIE KERBE WAR EIN AUFBAUFEHLER, kein Zeichenfehler. Zuerst hatte ich Blase und
-Zipfel als zwei getrennte Formen mit `outline` gezeichnet und danach versucht,
-die Kante zwischen ihnen mit einem schwarzen Strich wegzuradieren. Genau dort
-klaffte die Kerbe: zwei Umrisse, die sich beruehren, haben an der Naht immer
-eine.
-
-RICHTIG IST EINE SILHOUETTE. Blase und Zipfel werden zu EINER gefuellten Form
-vereinigt, dieselbe Form ein zweites Mal nach innen versetzt gezeichnet, und die
-Differenz IST der Umriss. Damit kann an der Naht nichts klaffen — es gibt keine
-Naht mehr.
-
-Der innere Rand: das Rechteck exakt um die Strichbreite eingezogen und der
-Eckradius um denselben Betrag verkleinert (so gehoert es sich bei abgerundeten
-Rechtecken). Der Zipfel wird um den Blasenmittelpunkt skaliert — bei einer
-kompakten Form ergibt das einen optisch gleichmaessigen Strich, und die
-Ueberlappung mit dem inneren Rechteck haelt die Form geschlossen.
-"""
-import pathlib
-from PIL import Image, ImageChops, ImageDraw, ImageFont
-
-APP = pathlib.Path(r'C:\KI-Workstation\BitDM\secure-messenger\app')
-S, U = 1024, 4
-N = S * U
-SCHWARZ = (11, 12, 18, 255)
-WEISS = (243, 243, 246, 255)
-
-BX0, BY0, BX1, BY1, RAD = 176, 206, 848, 700, 126
-STRICH = 48
-ZIPFEL = [(302, 636), (474, 636), (302, 852)]
-CX, CY = (BX0 + BX1) / 2, (BY0 + BY1) / 2
-
-
-def k(v):
- return round(v * U)
-
-
-def silhouette(einzug):
- """Blase + Zipfel als eine gefuellte Maske, um [einzug] nach innen."""
- m = Image.new('L', (N, N), 0)
- d = ImageDraw.Draw(m)
- d.rounded_rectangle(
- (k(BX0 + einzug), k(BY0 + einzug), k(BX1 - einzug), k(BY1 - einzug)),
- radius=k(max(RAD - einzug, 2)), fill=255)
- if einzug:
- sx = (BX1 - BX0 - 2 * einzug) / (BX1 - BX0)
- sy = (BY1 - BY0 - 2 * einzug) / (BY1 - BY0)
- z = [(CX + (x - CX) * sx, CY + (y - CY) * sy) for x, y in ZIPFEL]
- else:
- z = ZIPFEL
- d.polygon([(k(x), k(y)) for x, y in z], fill=255)
- return m
-
-
-umriss = ImageChops.subtract(silhouette(0), silhouette(STRICH))
-
-bild = Image.new('RGBA', (N, N), (0, 0, 0, 0))
-d = ImageDraw.Draw(bild)
-d.rounded_rectangle((0, 0, N - 1, N - 1), radius=k(278), fill=SCHWARZ)
-bild.paste(Image.new('RGBA', (N, N), WEISS), (0, 0), umriss)
-
-# Das B in Doto — NACH DER TINTE GESETZT, nicht nach den Schriftmassen.
-#
-# Erst hatte ich mit `textbbox` zentriert. Gemessen kam das B dann 19 Pixel zu
-# weit links und 14 zu hoch heraus und fuellte nur 32 % der Innenbreite: Doto
-# ist eine Punktmatrix-Schrift und meldet ihre Glyphenmasse ungenau, wie bei
-# dieser Art Schrift ueblich.
-#
-# Darum: Buchstabe auf eine eigene Ebene, mit `getbbox()` auf die tatsaechliche
-# Tinte beschneiden, und dieses Stueck mittig einsetzen. Unabhaengig davon, was
-# die Schrift ueber sich behauptet.
-ebene = Image.new('RGBA', (N, N), (0, 0, 0, 0))
-ImageDraw.Draw(ebene).text(
- (k(200), k(200)), 'B',
- font=ImageFont.truetype(str(APP / 'assets/fonts/Doto.ttf'), k(470)),
- fill=WEISS)
-tinte = ebene.crop(ebene.getbbox())
-zx = round(CX * U - tinte.width / 2)
-zy = round(CY * U - tinte.height / 2)
-bild.paste(tinte, (zx, zy), tinte)
-print(f' B-Tinte {tinte.width // U}x{tinte.height // U} px, '
- f'Fuellung {100 * tinte.height / U / 398:.0f} % der Innenhoehe')
-
-bild = bild.resize((S, S), Image.LANCZOS)
-bild.save(APP / 'icon_bitdm_1024.png')
-print('gezeichnet, Strichbreite', STRICH)
-print(' Ecke ', bild.getpixel((3, 3)))
-print(' Blasenkante ', bild.getpixel((S // 2, round(BY0 * S / 1024) + 6)))
-print(' Blaseninnen ', bild.getpixel((S // 2, round((BY0 + 90) * S / 1024))))
diff --git a/secure-messenger/app/tool/symbol/02_klein.py b/secure-messenger/app/tool/symbol/02_klein.py
deleted file mode 100644
index e8e97b6..0000000
--- a/secure-messenger/app/tool/symbol/02_klein.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Kleinfassung fuer 16 bis 32 Pixel.
-
-BEI 24 PIXELN VERSCHMELZEN DIE PUNKTE. Gemessen an der Groessenprobe: ab 48 ist
-das Doto-B lesbar, darunter wird es ein Fleck, und der 48er Umriss wird bei
-16 Pixeln 0,75 Pixel dick — also grau statt weiss.
-
-Darum fuer die kleinen Groessen: dieselbe Silhouette (Blase mit Zipfel als
-Umriss, damit das Symbol nicht die Form wechselt), aber kraeftiger Strich und
-ein VOLLES B aus Chivo Mono statt der Punktmatrix. So bleibt bei 16 Pixeln ein
-erkennbarer Buchstabe stehen.
-
-Die .ico traegt beides: klein diese Fassung, gross die mit den Punkten.
-"""
-import pathlib
-from PIL import Image, ImageChops, ImageDraw, ImageFont
-
-APP = pathlib.Path(r'C:\KI-Workstation\BitDM\secure-messenger\app')
-S, U = 256, 8
-N = S * U
-SCHWARZ = (11, 12, 18, 255)
-WEISS = (243, 243, 246, 255)
-
-# Entwurf im 256er Raster, damit die Zahlen zur Zielgroesse passen.
-BX0, BY0, BX1, BY1, RAD = 30, 40, 226, 172, 30
-STRICH = 20 # deutlich dicker als 48/1024
-ZIPFEL = [(64, 150), (110, 150), (64, 214)]
-CX, CY = (BX0 + BX1) / 2, (BY0 + BY1) / 2
-
-
-def k(v):
- return round(v * U)
-
-
-def sil(ein):
- m = Image.new('L', (N, N), 0)
- d = ImageDraw.Draw(m)
- d.rounded_rectangle((k(BX0 + ein), k(BY0 + ein), k(BX1 - ein), k(BY1 - ein)),
- radius=k(max(RAD - ein, 2)), fill=255)
- if ein:
- sx = (BX1 - BX0 - 2 * ein) / (BX1 - BX0)
- sy = (BY1 - BY0 - 2 * ein) / (BY1 - BY0)
- z = [(CX + (x - CX) * sx, CY + (y - CY) * sy) for x, y in ZIPFEL]
- else:
- z = ZIPFEL
- d.polygon([(k(x), k(y)) for x, y in z], fill=255)
- return m
-
-
-b = Image.new('RGBA', (N, N), (0, 0, 0, 0))
-ImageDraw.Draw(b).rounded_rectangle((0, 0, N - 1, N - 1),
- radius=k(69.5), fill=SCHWARZ)
-b.paste(Image.new('RGBA', (N, N), WEISS), (0, 0),
- ImageChops.subtract(sil(0), sil(STRICH)))
-
-# Volles B, wieder nach der Tinte gesetzt statt nach den Schriftmassen.
-e = Image.new('RGBA', (N, N), (0, 0, 0, 0))
-ImageDraw.Draw(e).text((k(60), k(50)), 'B',
- font=ImageFont.truetype(
- str(APP / 'assets/fonts/ChivoMono.ttf'), k(96)),
- fill=WEISS)
-t = e.crop(e.getbbox())
-b.paste(t, (round(CX * U - t.width / 2), round(CY * U - t.height / 2)), t)
-
-b = b.resize((S, S), Image.LANCZOS)
-b.save(APP / 'icon_bitdm_klein.png')
-print(f'Kleinfassung {b.size}, B-Tinte {t.width // U}x{t.height // U} px im '
- f'256er Raster')
diff --git a/secure-messenger/app/tool/symbol/03_verteilen.py b/secure-messenger/app/tool/symbol/03_verteilen.py
deleted file mode 100644
index 23083bd..0000000
--- a/secure-messenger/app/tool/symbol/03_verteilen.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Aus icon_bitdm_1024.png alle Ziele schreiben."""
-import pathlib
-from PIL import Image
-
-APP = pathlib.Path(r'C:\KI-Workstation\BitDM\secure-messenger\app')
-q = Image.open(APP / 'icon_bitdm_1024.png').convert('RGBA')
-
-
-def s(n):
- return q.resize((n, n), Image.LANCZOS)
-
-
-res = APP / 'android/app/src/main/res'
-for ordner, n in [('mdpi', 48), ('hdpi', 72), ('xhdpi', 96),
- ('xxhdpi', 144), ('xxxhdpi', 192)]:
- s(n).save(res / f'mipmap-{ordner}' / 'ic_launcher.png')
-print('android : 48 72 96 144 192')
-
-ico = APP / 'windows/runner/resources/app_icon.ico'
-s(256).save(ico, sizes=[(16, 16), (24, 24), (32, 32), (48, 48),
- (64, 64), (128, 128), (256, 256)])
-print('windows : .ico mit 7 Groessen')
-
-web = APP / 'web'
-s(32).save(web / 'favicon.png')
-s(192).save(web / 'icons/Icon-192.png')
-s(512).save(web / 'icons/Icon-512.png')
-
-# Maskierbar: Chrome legt eine eigene Form darueber und schneidet bis zu 20 %
-# weg. Vollflaechig dunkel hinterlegt und das Motiv eingeruecht, sonst faellt
-# die Rundung dem fremden Zuschnitt zum Opfer.
-for n in (192, 512):
- g = Image.new('RGBA', (n, n), (11, 12, 18, 255))
- innen = int(n * 0.74)
- k = s(innen)
- g.paste(k, ((n - innen) // 2, (n - innen) // 2), k)
- g.save(web / f'icons/Icon-maskable-{n}.png')
-print('web : favicon 32, 192, 512, maskierbar 192+512')
-
-# Fuer die Webseite, damit der Reiter im Browser nicht leer bleibt.
-s(180).save(APP.parent / 'website/bilder/icon-180.png')
-print('website : bilder/icon-180.png')
diff --git a/secure-messenger/app/tool/symbol/README.md b/secure-messenger/app/tool/symbol/README.md
index 2a797e1..f83497f 100644
--- a/secure-messenger/app/tool/symbol/README.md
+++ b/secure-messenger/app/tool/symbol/README.md
@@ -1,31 +1,37 @@
-# Das App-Symbol neu bauen
+# Das BitDM-Zeichen bauen
-Reihenfolge, sonst fehlt die Vorlage:
+Seit 24.10.2026 gilt Entwurf **F1, „Anzeigefenster“**: ein Displayfenster auf
+der Gerätefront, darin eine Punktmatrix, und DM leuchtet in Violett. Henrik hat
+es aus sechs Entwürfen und sechs Varianten gewählt. Es löst die Sprechblase mit
+dem B ab (`65fd2dc`), die schwarz-weiß war und neben dem Favicon ein zweites
+Zeichen bildete.
```bash
-py tool/symbol/01_gross.py # icon_bitdm_1024.png — Blase + Doto-B
-py tool/symbol/02_klein.py # icon_bitdm_klein.png — kraeftiger, volles B
-py tool/symbol/03_verteilen.py # Android, Web, Webseite
+py tool/symbol/verteilen.py
```
-Danach die `.ico` gemischt schreiben — klein bis 32, Punktmatrix ab 48:
+schreibt alles neu, aus einer einzigen Vorlage in `f1.py`:
-```python
-gr = Image.open('icon_bitdm_1024.png'); kl = Image.open('icon_bitdm_klein.png')
-t = [kl.resize((n, n)) for n in (16, 24, 32)] + \
- [gr.resize((n, n)) for n in (48, 64, 128, 256)]
-t[-1].save('windows/runner/resources/app_icon.ico',
- sizes=[x.size for x in t], append_images=t[:-1])
-```
+| Ziel | Fassung |
+|---|---|
+| Android `mipmap-*/ic_launcher.png` (48–192) | groß |
+| Windows `app_icon.ico` | 16–32 klein, 48–256 groß |
+| Web-App `web/favicon.png` (32) | klein |
+| Web-App `web/icons/Icon-*.png`, maskierbar | groß |
+| F-Droid `fastlane/.../images/icon.png` (512) | groß |
+| Website `bilder/icon-180.png`, `bilder/bitdm-zeichen.svg` | groß |
+| Website `favicon.ico` | 16/32 klein, 48 groß |
+| Website `favicon.svg` | klein |
-**Warum zwei Fassungen.** Gemessen an einer Groessenprobe: das Doto-B ist ab
-48 Pixeln lesbar, darunter verschmelzen die Punkte, und der Umriss wird bei 16
-Pixeln 0,75 Pixel dick — also grau statt weiss. Eine `.ico` darf pro Groesse
-ein eigenes Bild tragen, also traegt sie klein die kraeftige Fassung.
+**Warum zwei Fassungen.** Unter 48 Pixeln werden Fensterrahmen und
+unbeleuchtete Punkte zu grauem Brei, bei 16 Pixeln wäre ein Punkt 0,7 Pixel
+breit. Die Kleinfassung zeigt dasselbe DM ohne Fenster und Raster, mit Punkten,
+die fast aneinanderstoßen. So bleibt es bei 16 Pixeln eine lesbare Pixelschrift.
+Dieselbe Grenze galt schon für das alte Zeichen; damals gemessen an einer
+Größenprobe, jetzt erneut nachgesehen.
-**Was NICHT nochmal probiert werden muss** (alles am 30.07.2026 gebaut und
-verworfen): den gefalteten Rhombus der ersten Vorlage nachzuzeichnen — die
-Facetten werden bei 48 Pixeln Brei; Kreise mit Rechteck-Schnitt fuer die Drei —
-das beisst ein quadratisches Loch in die Form; `textbbox` zum Zentrieren von
-Doto — die Schrift meldet ihre Glyphenmasse ungenau, das B sass 19 Pixel zu
-weit links. Zentriert wird nach `getbbox()` der gezeichneten Tinte.
+**Maße.** Alles steht im 108er Raster eines Android-Symbols: Kachelradius 24,
+Fenster 10/22 bis 98/86 mit Radius 10, Raster 13 × 9 Punkte im Abstand 6,4. DM
+sitzt eine Zelle eingerückt auf demselben Raster, die leuchtenden Punkte liegen
+also genau über unbeleuchteten. Farben aus `DESIGN.md`: Grund `#0b0c12`, Linie
+`#3f424d`, Akzent `#b5abfc`, aus `#1f1d31`.
diff --git a/secure-messenger/app/tool/symbol/f1.py b/secure-messenger/app/tool/symbol/f1.py
new file mode 100644
index 0000000..8e8de90
--- /dev/null
+++ b/secure-messenger/app/tool/symbol/f1.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+"""Das BitDM-Zeichen seit 24.10.2026: Entwurf F1, "Anzeigefenster".
+
+Ein Displayfenster auf der Geraetefront, darin eine Punktmatrix. Die meisten
+Punkte sind aus, DM leuchtet. Henrik hat es aus sechs Entwuerfen gewaehlt; es
+loest die Sprechblase mit dem B ab (65fd2dc), die schwarz-weiss war, das
+Violett der App nicht kannte und neben dem Favicon (fuenf Punkte) ein zweites
+Zeichen war.
+
+ALLES STEHT IM 108er RASTER, dem Mass eines Android-Symbols. Daraus rechnen
+Pixelbilder und SVG dieselbe Form, damit es nur EINE Vorlage gibt.
+
+ZWEI FASSUNGEN, wie schon beim alten Zeichen gemessen: unter 48 Pixeln werden
+Fensterrahmen (1,5 Einheiten) und die unbeleuchteten Punkte zu grauem Brei, bei
+16 Pixeln waere ein Punkt 0,7 Pixel breit. Darum bis 32 Pixel `klein`: dasselbe
+DM, ohne Fenster und Raster, die Punkte so gross, dass sie fast aneinander
+stossen. Das bleibt bei 16 Pixeln ein lesbares DM in Pixelschrift.
+"""
+from PIL import Image, ImageDraw
+
+GRUND = (11, 12, 18, 255) # #0b0c12, Seitenhintergrund (DESIGN.md)
+LINIE = (63, 66, 77, 255) # #3f424d, Hairline/Bezelkante
+AUS = (31, 29, 49, 255) # #1f1d31, unbeleuchteter Punkt
+AN = (181, 171, 252, 255) # #b5abfc, Akzent: die Anzeige leuchtet
+
+D = ['xxxx.', 'x...x', 'x...x', 'x...x', 'x...x', 'x...x', 'xxxx.']
+M = ['x...x', 'xx.xx', 'x.x.x', 'x.x.x', 'x...x', 'x...x', 'x...x']
+DM = [d + '.' + m for d, m in zip(D, M)] # 11 Spalten, 7 Zeilen
+
+KACHEL_RADIUS = 24
+# gross: Fenster, 13 x 9 Raster, DM eine Zelle eingerueckt
+FENSTER = (10, 22, 98, 86)
+FENSTER_RADIUS, FENSTER_STRICH = 10, 1.5
+RASTER = dict(abstand=6.4, x0=15.6, y0=28.4, spalten=13, zeilen=9, r_aus=2.2, r_an=2.4)
+# klein: nur DM, gross gesetzt
+KLEIN = dict(abstand=8.2, x0=13.0, y0=29.4, r=3.6)
+
+
+def _hex(farbe):
+ return '#%02x%02x%02x' % farbe[:3]
+
+
+def _punkte(zeilen, abstand, x0, y0):
+ """Mittelpunkte der leuchtenden Punkte in Rastereinheiten."""
+ for y, zeile in enumerate(zeilen):
+ for x, c in enumerate(zeile):
+ if c == 'x':
+ yield x0 + x * abstand, y0 + y * abstand
+
+
+def zeichne(n, klein=False, ueberabtastung=4):
+ """Das Zeichen als RGBA-Bild n x n, mit durchsichtigen Ecken."""
+ s = n * ueberabtastung
+ k = s / 108
+ bild = Image.new('RGBA', (s, s), (0, 0, 0, 0))
+ d = ImageDraw.Draw(bild)
+ d.rounded_rectangle((0, 0, s - 1, s - 1), radius=round(KACHEL_RADIUS * k), fill=GRUND)
+
+ def kreis(cx, cy, r, farbe):
+ d.ellipse(((cx - r) * k, (cy - r) * k, (cx + r) * k, (cy + r) * k), fill=farbe)
+
+ if klein:
+ for cx, cy in _punkte(DM, KLEIN['abstand'], KLEIN['x0'], KLEIN['y0']):
+ kreis(cx, cy, KLEIN['r'], AN)
+ else:
+ x0, y0, x1, y1 = FENSTER
+ d.rounded_rectangle((x0 * k, y0 * k, x1 * k, y1 * k), radius=round(FENSTER_RADIUS * k),
+ outline=LINIE, width=max(1, round(FENSTER_STRICH * k)))
+ a = RASTER['abstand']
+ for y in range(RASTER['zeilen']):
+ for x in range(RASTER['spalten']):
+ kreis(RASTER['x0'] + x * a, RASTER['y0'] + y * a, RASTER['r_aus'], AUS)
+ # DM sitzt eine Zelle eingerueckt auf demselben Raster: die leuchtenden
+ # Punkte liegen genau ueber unbeleuchteten.
+ for cx, cy in _punkte(DM, a, RASTER['x0'] + a, RASTER['y0'] + a):
+ kreis(cx, cy, RASTER['r_an'], AN)
+ return bild.resize((n, n), Image.LANCZOS)
+
+
+def svg(klein=False):
+ """Dasselbe Zeichen als SVG im 108er Raster."""
+ teile = [f'']
+ if klein:
+ teile += [f''
+ for cx, cy in _punkte(DM, KLEIN['abstand'], KLEIN['x0'], KLEIN['y0'])]
+ punkte = f'' + ''.join(teile[1:]) + ''
+ teile = teile[:1] + [punkte]
+ else:
+ x0, y0, x1, y1 = FENSTER
+ teile.append(f'')
+ a = RASTER['abstand']
+ aus = ''.join(f''
+ for y in range(RASTER['zeilen']) for x in range(RASTER['spalten']))
+ an = ''.join(f''
+ for cx, cy in _punkte(DM, a, RASTER['x0'] + a, RASTER['y0'] + a))
+ teile += [f'{aus}', f'{an}']
+ return ('\n')
diff --git a/secure-messenger/app/tool/symbol/verteilen.py b/secure-messenger/app/tool/symbol/verteilen.py
new file mode 100644
index 0000000..6486d9e
--- /dev/null
+++ b/secure-messenger/app/tool/symbol/verteilen.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"""Das Zeichen F1 an alle Stellen schreiben, an denen App und Seite es zeigen.
+
+Bis 32 Pixel die Kleinfassung, ab 48 das volle Anzeigefenster (siehe f1.py).
+Die .ico-Dateien tragen beides: pro Groesse ein eigenes Bild.
+"""
+import pathlib
+import sys
+
+from PIL import Image
+
+HIER = pathlib.Path(__file__).resolve().parent
+sys.path.insert(0, str(HIER))
+import f1 # noqa: E402
+
+APP = HIER.parent.parent
+SEITE = APP.parent / 'website'
+
+
+def gross(n):
+ return f1.zeichne(n)
+
+
+def klein(n):
+ return f1.zeichne(n, klein=True)
+
+
+def ico(ziel, groessen):
+ """Eine .ico, jede Groesse mit ihrer eigenen Fassung."""
+ bilder = [klein(n) if n <= 32 else gross(n) for n in groessen]
+ bilder[-1].save(ziel, sizes=[(n, n) for n in groessen], append_images=bilder[:-1])
+
+
+res = APP / 'android/app/src/main/res'
+for ordner, n in [('mdpi', 48), ('hdpi', 72), ('xhdpi', 96),
+ ('xxhdpi', 144), ('xxxhdpi', 192)]:
+ gross(n).save(res / f'mipmap-{ordner}' / 'ic_launcher.png')
+print('android : 48 72 96 144 192')
+
+ico(APP / 'windows/runner/resources/app_icon.ico', [16, 24, 32, 48, 64, 128, 256])
+print('windows : .ico, 16-32 klein, 48-256 gross')
+
+web = APP / 'web'
+klein(32).save(web / 'favicon.png')
+gross(192).save(web / 'icons/Icon-192.png')
+gross(512).save(web / 'icons/Icon-512.png')
+# Maskierbar: Chrome legt eine eigene Form darueber und schneidet bis zu 20 %
+# weg. Vollflaechig dunkel hinterlegt und das Motiv eingerueckt, sonst faellt
+# das Fenster dem fremden Zuschnitt zum Opfer.
+for n in (192, 512):
+ g = Image.new('RGBA', (n, n), f1.GRUND)
+ innen = int(n * 0.74)
+ z = gross(innen)
+ g.paste(z, ((n - innen) // 2, (n - innen) // 2), z)
+ g.save(web / f'icons/Icon-maskable-{n}.png')
+print('web : favicon 32 klein, 192, 512, maskierbar 192+512')
+
+gross(512).save(APP / 'fastlane/metadata/android/en-US/images/icon.png')
+print('fastlane: icon.png 512 (F-Droid-Eintrag)')
+
+gross(180).save(SEITE / 'bilder/icon-180.png')
+ico(SEITE / 'favicon.ico', [16, 32, 48])
+# newline='\n': sonst schreibt Windows CRLF, und git meldet die Dateien bei
+# jedem Lauf als geaendert.
+(SEITE / 'favicon.svg').write_text(f1.svg(klein=True), encoding='utf-8', newline='\n')
+(SEITE / 'bilder/bitdm-zeichen.svg').write_text(f1.svg(), encoding='utf-8', newline='\n')
+print('website : bilder/icon-180.png, favicon.ico (16/32 klein, 48 gross), '
+ 'favicon.svg (klein), bilder/bitdm-zeichen.svg (gross)')
diff --git a/secure-messenger/app/web/favicon.png b/secure-messenger/app/web/favicon.png
index a7f1d09..df65569 100644
Binary files a/secure-messenger/app/web/favicon.png and b/secure-messenger/app/web/favicon.png differ
diff --git a/secure-messenger/app/web/icons/Icon-192.png b/secure-messenger/app/web/icons/Icon-192.png
index f03a040..7acaf81 100644
Binary files a/secure-messenger/app/web/icons/Icon-192.png and b/secure-messenger/app/web/icons/Icon-192.png differ
diff --git a/secure-messenger/app/web/icons/Icon-512.png b/secure-messenger/app/web/icons/Icon-512.png
index 390a6a8..b9b3e90 100644
Binary files a/secure-messenger/app/web/icons/Icon-512.png and b/secure-messenger/app/web/icons/Icon-512.png differ
diff --git a/secure-messenger/app/web/icons/Icon-maskable-192.png b/secure-messenger/app/web/icons/Icon-maskable-192.png
index f25d29f..4f416e9 100644
Binary files a/secure-messenger/app/web/icons/Icon-maskable-192.png and b/secure-messenger/app/web/icons/Icon-maskable-192.png differ
diff --git a/secure-messenger/app/web/icons/Icon-maskable-512.png b/secure-messenger/app/web/icons/Icon-maskable-512.png
index 4048dad..7531f03 100644
Binary files a/secure-messenger/app/web/icons/Icon-maskable-512.png and b/secure-messenger/app/web/icons/Icon-maskable-512.png differ
diff --git a/secure-messenger/app/windows/runner/resources/app_icon.ico b/secure-messenger/app/windows/runner/resources/app_icon.ico
index 81a9be3..eb08bf8 100644
Binary files a/secure-messenger/app/windows/runner/resources/app_icon.ico and b/secure-messenger/app/windows/runner/resources/app_icon.ico differ
diff --git a/secure-messenger/website/README.md b/secure-messenger/website/README.md
index f1b569b..502f23e 100644
--- a/secure-messenger/website/README.md
+++ b/secure-messenger/website/README.md
@@ -17,6 +17,7 @@ website/
├─ sitemap.xml alle Seiten oben + llms.txt, je mit .html
├─ 1e0f…5274.txt IndexNow-Schlüssel (öffentlich mit Absicht, siehe unten)
├─ favicon.ico Seitenzeichen 16/32/48 px, favicon.svg dasselbe als Vektor
+├─ bilder/bitdm-zeichen.svg das volle Zeichen als Vektor (app/tool/symbol)
├─ bilder/ App-Icon
├─ fonts/ Doto + Chivo Mono (SIL OFL, lokal eingebettet)
└─ README.md
diff --git a/secure-messenger/website/bilder/bitdm-zeichen.svg b/secure-messenger/website/bilder/bitdm-zeichen.svg
new file mode 100644
index 0000000..87ed93a
--- /dev/null
+++ b/secure-messenger/website/bilder/bitdm-zeichen.svg
@@ -0,0 +1 @@
+
diff --git a/secure-messenger/website/bilder/icon-180.png b/secure-messenger/website/bilder/icon-180.png
index 3781979..569ed3a 100644
Binary files a/secure-messenger/website/bilder/icon-180.png and b/secure-messenger/website/bilder/icon-180.png differ
diff --git a/secure-messenger/website/favicon.ico b/secure-messenger/website/favicon.ico
index 12e9d09..f2c0a0f 100644
Binary files a/secure-messenger/website/favicon.ico and b/secure-messenger/website/favicon.ico differ
diff --git a/secure-messenger/website/favicon.svg b/secure-messenger/website/favicon.svg
index 73a09ac..66ffdc5 100644
--- a/secure-messenger/website/favicon.svg
+++ b/secure-messenger/website/favicon.svg
@@ -1 +1 @@
-
+