Stack docker-compose Roundcube 1.7.4 (nginx, php-fpm, MariaDB) avec skin vBlog.io

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sa4nUG3GtbdEaPS6fA8eq1
This commit is contained in:
claude BotandClaude Opus 5.5 committed 2026-10-02 18:16:20 +00:00
commit 965e537497
21 files changed
+669

No files matched your search

+49
View File
@@ -0,0 +1,49 @@
# Copier en .env puis adapter : cp .env.example .env
# Pas de commentaire en fin de ligne : Compose peut l'intégrer à la valeur.
COMPOSE_PROJECT_NAME=roundcube
# --- Versions (images épinglées) ---
# Tag roundcube/roundcubemail:<RC_VERSION>-fpm-alpine
RC_VERSION=1.7.4
# MariaDB LTS / nginx branche stable
MARIADB_VERSION=11.8
NGINX_VERSION=1.30-alpine
# --- Exposition HTTP (prévue derrière un reverse proxy TLS) ---
# 0.0.0.0 pour écouter sur toutes les interfaces
HTTP_BIND=127.0.0.1
HTTP_PORT=8080
# --- Base de données ---
# Générer avec : openssl rand -base64 24
DB_NAME=roundcubemail
DB_USER=roundcube
DB_PASSWORD=change-me
DB_ROOT_PASSWORD=change-me
# --- Clé de chiffrement Roundcube (des_key) ---
# 24 caractères exactement ; ne plus la changer ensuite. openssl rand -base64 18
RC_DES_KEY=change-me-24-characters!
# --- Serveurs de messagerie ---
# Préfixes : ssl:// (TLS implicite, 993/465), tls:// (STARTTLS, 143/587), aucun = clair
IMAP_HOST=ssl://mail.example.org
IMAP_PORT=993
SMTP_HOST=tls://mail.example.org
SMTP_PORT=587
# Domaine ajouté aux identifiants sans @ (ex. example.org), vide = aucun
USERNAME_DOMAIN=
# --- Plugins ---
# Plugins fournis avec Roundcube, à activer
RC_PLUGINS=archive,zipdownload,managesieve,markasjunk
# Paquets composer installés au démarrage (ex. johndoh/contextmenu), à activer aussi dans RC_PLUGINS
RC_COMPOSER_PLUGINS=
# --- Divers ---
# Taille max des pièces jointes : PHP (upload/post) et nginx (client_max_body_size)
UPLOAD_MAX_FILESIZE=25M
# Dictionnaires aspell intégrés à l'image (build), séparés par des espaces
ASPELL_DICTS=fr en
TZ=Europe/Paris
+1
View File
@@ -0,0 +1 @@
.env
+107
View File
@@ -0,0 +1,107 @@
# Roundcube Webmail en docker-compose
Stack Roundcube **1.7.4** prête à l'emploi : nginx + php-fpm + MariaDB, skin Elastic recompilé avec la charte vBlog.io (surcharges LESS dans `theme/`).
```
:8080 (127.0.0.1 par défaut, derrière un reverse proxy TLS)
│
┌─────────────▼─────────────┐ réseau frontend (sortie IMAP/SMTP)
│ nginx 1.30-alpine │ sert le statique depuis le volume www (ro)
└─────────────┬─────────────┘
│ FastCGI :9000
┌─────────────▼─────────────┐
│ roundcube (php-fpm 8.4) │ image locale = roundcube/roundcubemail:1.7.4-fpm-alpine
│ │ + skin Elastic recompilé + aspell + mailcap
└─────────────┬─────────────┘
│ réseau backend (internal: pas d'accès sortant)
┌─────────────▼─────────────┐
│ mariadb 11.8 (LTS) │
└───────────────────────────┘
```
![Login](docs/apercu-login.png)
## Démarrage
```sh
cp .env.example .env # renseigner mots de passe, RC_DES_KEY (24 car.), IMAP/SMTP
docker compose up -d --build
docker compose ps # les 3 services doivent passer "healthy"
```
Puis `http://127.0.0.1:8080/`. Le premier démarrage copie les sources dans le volume `www` et crée le schéma SQL (`bin/initdb.sh`), compter ~30 s.
## Arborescence
| Chemin | Rôle |
|---|---|
| `compose.yaml` | Les 3 services, volumes, réseaux, healthchecks |
| `.env.example` | Toutes les variables (versions, BDD, IMAP/SMTP, plugins...) |
| `roundcube/Dockerfile` | Build multi-étage : compilation LESS (node) puis image finale |
| `roundcube/zz-healthcheck.conf` | `ping.path` php-fpm pour le healthcheck |
| `nginx/templates/default.conf.template` | vhost, rendu par l'entrypoint nginx (envsubst `NGINX_*`) |
| `config/*.php` | Config Roundcube additionnelle, montée sur `/var/roundcube/config` |
| `theme/` | Surcharges du skin Elastic (`_variables.less`, `_styles.less`, `fonts/`, `images/`) |
| `scripts/sync-theme.sh` | Réimporte une customisation (défaut `../roundcube-custom/vblog`) |
## Choix techniques
- **Image officielle `fpm-alpine`** ([roundcube/roundcubemail-docker](https://github.com/roundcube/roundcubemail-docker)) : son entrypoint génère `config.docker.inc.php` depuis les `ROUNDCUBEMAIL_*`, inclut `/var/roundcube/config/*.php`, initialise/migre la base. Pas de réimplémentation.
- **Volume `www` partagé** (rw côté Roundcube, ro côté nginx) : pattern documenté upstream pour la variante FPM. À chaque démarrage, l'entrypoint resynchronise `/usr/src/roundcubemail` vers le volume (`bin/installto.sh`) : une image reconstruite (version ou thème) est donc propagée au simple `up -d`.
- **nginx** : depuis 1.7, seul `public_html/` est le docroot et les ressources sont appelées en `/static.php/<chemin>`. Le vhost les sert directement depuis le volume (préfixes et extensions alignés sur `ALLOWED_PATHS`/`SUPPORTED_TYPES` de `static.php`, cache 30 j, les URL portent `?s=<mtime>`). Seuls `index.php` et `static.php` atteignent PHP ; `installer.php` et le reste renvoient 404.
- **Thème compilé au build** : Elastic est livré en CSS minifié ; `_variables.less`/`_styles.less` ne sont pris en compte qu'à la compilation (`@import (optional)` dans `styles/variables.less` et `styles/styles.less`). Un étage `node:22-alpine` recompile `styles`, `print` et `embed` avec `lessc` (même commande que `skins/elastic/Makefile`). Aucune dépendance Node dans l'image finale.
- **Dictionnaires aspell et `mailcap` intégrés au build** plutôt qu'installés à chaque démarrage (`ROUNDCUBEMAIL_ASPELL_DICTS` fait un `apk add` au runtime). `mailcap` fournit `/etc/mime.types`, absent de l'image upstream (sinon warning « Mimetype to file extension mapping doesn't work properly »).
- **`des_key` imposée par `.env`** : sinon l'entrypoint la génère aléatoirement dans `config.inc.php`, perdue si le volume `www` est recréé.
- **Healthchecks** : MariaDB `healthcheck.sh --connect --innodb_initialized` ; Roundcube : port FPM 9000 ; nginx : `GET /fpm-ping` relayé à php-fpm (chaîne nginx → FPM validée, URL limitée à 127.0.0.1). Ordre de démarrage piloté par `depends_on: condition: service_healthy`.
- **Réseau `backend` en `internal: true`** : MariaDB n'a ni port publié ni accès sortant.
## Thème vBlog.io
Les fichiers de `theme/` et `config/20-vblog.inc.php` sont une copie de `roundcube-custom/vblog/`. Pour resynchroniser après modification :
```sh
sh scripts/sync-theme.sh [dossier_source]
docker compose up -d --build
```
Pour revenir à l'Elastic d'origine : vider `theme/`, supprimer `config/20-vblog.inc.php`, rebuild.
## Configuration
- **Variables** : voir `.env.example` (commentaires sur leur propre ligne : Compose peut intégrer un commentaire de fin de ligne à la valeur).
- **Options Roundcube** : déposer des `*.php` dans `config/` (inclus par ordre alphabétique), puis `docker compose restart roundcube`. Référence : `config/defaults.inc.php` dans le conteneur. `config/10-base.inc.php` fixe langue, `proxy_whitelist`, durée de session, rate-limit de login.
- **Plugins** : intégrés → `RC_PLUGINS` ; tiers → `RC_COMPOSER_PLUGINS` (installés par composer au démarrage, nécessite l'accès à packagist) + ajout dans `RC_PLUGINS`.
- **Taille des pièces jointes** : `UPLOAD_MAX_FILESIZE` applique la même limite à PHP et nginx. Le message encodé en base64 pèse ~1,37× la pièce jointe.
## Reverse proxy TLS
La stack écoute en HTTP sur `127.0.0.1:8080`. Le proxy frontal (Traefik, HAProxy, nginx...) doit transmettre `X-Forwarded-For` et `X-Forwarded-Proto: https`. Restreindre `proxy_whitelist` (`config/10-base.inc.php`) à l'IP du proxy en production. Si Roundcube est publié sous un sous-chemin, régler `ROUNDCUBEMAIL_REQUEST_PATH`.
## Exploitation
```sh
docker compose logs -f roundcube # logs Roundcube et php-fpm (log_driver = stdout)
# Sauvegarde : base + volume enigma si le plugin est utilisé
docker compose exec -T mariadb sh -c 'mariadb-dump -uroot -p"$MARIADB_ROOT_PASSWORD" --single-transaction "$MARIADB_DATABASE"' > roundcube-$(date +%F).sql
```
**Mise à jour Roundcube** : modifier `RC_VERSION` dans `.env` puis `docker compose up -d --build`. L'entrypoint exécute `installto.sh` puis `initdb.sh --update` (migrations SQL). Sauvegarder la base avant.
**Proxy TLS d'entreprise pendant le build** (npm, apk) : fournir sa CA en secret BuildKit, optionnel :
```sh
docker build -f roundcube/Dockerfile --secret id=ca,src=/chemin/ca.pem -t local/roundcube:1.7.4 .
docker compose up -d --no-build
```
## Validation effectuée
Stack construite et démarrée avec Docker 29.6 / Compose 5.3 : 3 services `healthy`, schéma SQL créé, login IMAP réussi contre un Dovecot 2.4.5 de test (utilisateur enregistré en base), charte vBlog appliquée (captures `docs/`), `installer.php` et `static.php/config/...` en 404, statique servi par nginx avec cache. Non testé dans l'environnement de validation (dépôt Alpine bloqué) : l'étape `apk add` des dictionnaires/mailcap, et l'envoi SMTP.
## Sources
- Image et entrypoint : https://github.com/roundcube/roundcubemail-docker et https://hub.docker.com/r/roundcube/roundcubemail
- Options de config : https://github.com/roundcube/roundcubemail/blob/master/config/defaults.inc.php
- Skin Elastic, points d'extension LESS : https://github.com/roundcube/roundcubemail/tree/master/skins/elastic
- Image nginx (templates envsubst) : https://hub.docker.com/_/nginx ; image MariaDB (`healthcheck.sh`) : https://hub.docker.com/_/mariadb
+105
View File
@@ -0,0 +1,105 @@
name: ${COMPOSE_PROJECT_NAME:-roundcube}
# Roundcube Webmail : nginx (frontal) -> php-fpm (Roundcube) -> MariaDB
# Variables : voir .env.example
services:
mariadb:
image: mariadb:${MARIADB_VERSION:-11.8}
restart: unless-stopped
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
environment:
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?DB_ROOT_PASSWORD manquant dans .env}
MARIADB_DATABASE: ${DB_NAME:-roundcubemail}
MARIADB_USER: ${DB_USER:-roundcube}
MARIADB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD manquant dans .env}
MARIADB_AUTO_UPGRADE: "1"
volumes:
- db:/var/lib/mysql
networks: [backend]
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
roundcube:
build:
context: .
dockerfile: roundcube/Dockerfile
args:
RC_VERSION: ${RC_VERSION:-1.7.4}
ASPELL_DICTS: ${ASPELL_DICTS:-fr en}
image: ${RC_IMAGE:-local/roundcube}:${RC_VERSION:-1.7.4}
restart: unless-stopped
depends_on:
mariadb:
condition: service_healthy
environment:
ROUNDCUBEMAIL_DB_TYPE: mysql
ROUNDCUBEMAIL_DB_HOST: mariadb
ROUNDCUBEMAIL_DB_PORT: 3306
ROUNDCUBEMAIL_DB_NAME: ${DB_NAME:-roundcubemail}
ROUNDCUBEMAIL_DB_USER: ${DB_USER:-roundcube}
ROUNDCUBEMAIL_DB_PASSWORD: ${DB_PASSWORD}
ROUNDCUBEMAIL_DES_KEY: ${RC_DES_KEY:?RC_DES_KEY manquant dans .env (24 caractères)}
ROUNDCUBEMAIL_DEFAULT_HOST: ${IMAP_HOST:?IMAP_HOST manquant dans .env}
ROUNDCUBEMAIL_DEFAULT_PORT: ${IMAP_PORT:-993}
ROUNDCUBEMAIL_SMTP_SERVER: ${SMTP_HOST:?SMTP_HOST manquant dans .env}
ROUNDCUBEMAIL_SMTP_PORT: ${SMTP_PORT:-587}
ROUNDCUBEMAIL_USERNAME_DOMAIN: ${USERNAME_DOMAIN:-}
ROUNDCUBEMAIL_PLUGINS: ${RC_PLUGINS:-archive,zipdownload,managesieve,markasjunk}
ROUNDCUBEMAIL_COMPOSER_PLUGINS: ${RC_COMPOSER_PLUGINS:-}
ROUNDCUBEMAIL_SKIN: elastic
ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE: ${UPLOAD_MAX_FILESIZE:-25M}
TZ: ${TZ:-Europe/Paris}
volumes:
- www:/var/www/html # sources Roundcube (partagées avec nginx)
- ./config:/var/roundcube/config:ro # surcharges PHP (*.php inclus dans l'ordre alpha)
- temp:/tmp/roundcube-temp
- enigma:/var/roundcube/enigma # clés OpenPGP si plugin enigma
networks: [backend, frontend] # frontend = sortie vers IMAP/SMTP
healthcheck:
test: ["CMD", "php", "-r", "exit(@fsockopen('127.0.0.1', 9000) ? 0 : 1);"]
interval: 10s
timeout: 5s
retries: 6
start_period: 120s # 1er démarrage : copie des sources + initdb
nginx:
image: nginx:${NGINX_VERSION:-1.30-alpine}
restart: unless-stopped
depends_on:
roundcube:
condition: service_healthy
environment:
NGINX_PHP_FPM: roundcube:9000
NGINX_CLIENT_MAX_BODY_SIZE: ${UPLOAD_MAX_FILESIZE:-25M}
NGINX_ENVSUBST_FILTER: ^NGINX_ # n'interpole que nos variables
TZ: ${TZ:-Europe/Paris}
volumes:
- www:/var/www/html:ro
- ./nginx/templates:/etc/nginx/templates:ro
ports:
- "${HTTP_BIND:-127.0.0.1}:${HTTP_PORT:-8080}:80"
networks: [frontend]
healthcheck:
test: ["CMD-SHELL", "wget -q -O - http://127.0.0.1/fpm-ping | grep -q pong"]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
volumes:
db:
www:
temp:
enigma:
networks:
backend:
internal: true # MariaDB sans accès sortant ni exposition
frontend:
+21
View File
@@ -0,0 +1,21 @@
<?php
// Surcharges Roundcube communes. Tout fichier *.php de ce dossier est inclus
// (ordre alphabétique) après la config générée depuis les variables d'environnement.
// Référence : config/defaults.inc.php dans l'image (/var/www/html/config/defaults.inc.php)
$config['language'] = 'fr_FR';
$config['timezone'] = 'auto';
// Derrière un reverse proxy TLS : faire confiance aux en-têtes X-Forwarded-*
// des réseaux Docker / du proxy (à restreindre à l'IP du proxy en production)
$config['proxy_whitelist'] = ['172.16.0.0/12', '192.168.0.0/16', '10.0.0.0/8'];
$config['use_https'] = false; // true si le proxy n'envoie pas X-Forwarded-Proto
// Session / sécurité
$config['session_lifetime'] = 30; // minutes
$config['login_rate_limit'] = 3; // tentatives par minute et par IP
$config['ip_check'] = false; // true si IP client stable (casse derrière NAT/mobile)
// Validation des certificats IMAP/SMTP (laisser actif ; désactiver uniquement pour un lab auto-signé)
// $config['imap_conn_options'] = ['ssl' => ['verify_peer' => false, 'verify_peer_name' => false]];
// $config['smtp_conn_options'] = ['ssl' => ['verify_peer' => false, 'verify_peer_name' => false]];
+20
View File
@@ -0,0 +1,20 @@
<?php
// À fusionner dans config/config.inc.php
// Réf. options : config/defaults.inc.php (product_name, skin_logo, support_url)
$config['product_name'] = 'vLab Webmail';
// Chemins commençant par "/" = relatifs au dossier du skin (skins/elastic/)
// Logo vblog.io (emblème) :
// - version claire : logo marine/orange, pour la page de login en mode clair
// - version blanche : pour le menu latéral (fond marine) et le mode sombre
$config['skin_logo'] = [
'elastic:login' => '/images/vblog-logo.svg',
'elastic:login[dark]' => '/images/vblog-logo-white.svg',
'elastic:*' => '/images/vblog-logo-white.svg',
'elastic:*[dark]' => '/images/vblog-logo-white.svg',
'elastic:*[link]' => 'https://vblog.io',
];
// Lien "Aide" vers le blog (optionnel)
// $config['support_url'] = 'https://vblog.io';
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+65
View File
@@ -0,0 +1,65 @@
# Roundcube 1.7 : seul public_html/ est exposé (index.php, static.php).
# Les ressources statiques sont référencées en /static.php/<chemin> ; nginx les
# sert directement depuis le volume partagé et ne délègue à PHP que le reste.
upstream roundcube_fpm {
server ${NGINX_PHP_FPM};
}
server {
listen 80 default_server;
server_name _;
root /var/www/html/public_html;
index index.php;
client_max_body_size ${NGINX_CLIENT_MAX_BODY_SIZE};
server_tokens off;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "same-origin" always;
# Statique servi par nginx (même liste de préfixes que ALLOWED_PATHS de static.php)
location ~ ^/static\.php/((?:skins|plugins|program/js|program/resources)/.+\.(?:css|js|map|png|gif|jpe?g|svg|ico|webp|avif|woff2?|ttf|eot|html|json))$ {
alias /var/www/html/$1;
expires 30d;
add_header X-Content-Type-Options "nosniff" always;
access_log off;
}
location = /favicon.ico {
alias /var/www/html/skins/elastic/images/favicon.ico;
access_log off;
}
# Points d'entrée PHP autorisés (installer.php volontairement exclu)
location ~ ^/(?:index|static)\.php(?:/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTP_PROXY "";
fastcgi_pass roundcube_fpm;
fastcgi_read_timeout 300s;
}
# Healthcheck : ping du pool php-fpm, local uniquement
location = /fpm-ping {
allow 127.0.0.1;
deny all;
access_log off;
include fastcgi_params;
fastcgi_param SCRIPT_NAME /fpm-ping;
fastcgi_param SCRIPT_FILENAME /fpm-ping;
fastcgi_pass roundcube_fpm;
}
location = / {
rewrite ^ /index.php last;
}
# Tout le reste (installer.php, fichiers cachés...) : refusé
location / {
return 404;
}
}
+52
View File
@@ -0,0 +1,52 @@
# syntax=docker/dockerfile:1
# Image Roundcube (php-fpm) + skin Elastic recompilé avec les surcharges de theme/
#
# Le skin Elastic est livré compilé (styles.min.css). Ses points d'extension
# _variables.less / _styles.less ne sont pris en compte qu'à la compilation LESS :
# on recompile donc ici, dans un étage Node jetable, puis on recopie le skin
# dans /usr/src/roundcubemail (source que l'entrypoint synchronise vers /var/www/html).
ARG RC_VERSION=1.7.4
FROM roundcube/roundcubemail:${RC_VERSION}-fpm-alpine AS upstream
# --- Étage 1 : compilation LESS du skin Elastic ---
FROM node:22-alpine AS theme
ARG LESS_VERSION=4.9.1
ARG LESS_CLEANCSS_VERSION=1.6.0
# Secret optionnel "ca" : CA d'un proxy TLS d'entreprise (docker build --secret id=ca,src=ca.pem)
RUN --mount=type=secret,id=ca,required=false \
NODE_EXTRA_CA_CERTS=/run/secrets/ca npm install -g --no-fund --no-audit \
less@${LESS_VERSION} less-plugin-clean-css@${LESS_CLEANCSS_VERSION}
COPY --from=upstream /usr/src/roundcubemail/skins/elastic /build/elastic
COPY theme/ /build/theme/
WORKDIR /build/elastic
# Surcharges optionnelles : un theme/ vide redonne l'Elastic d'origine
RUN set -eu; \
for f in _variables.less _styles.less; do \
if [ -f /build/theme/$f ]; then cp /build/theme/$f styles/; echo "surcharge: $f"; fi; \
done; \
if [ -d /build/theme/fonts ]; then cp -r /build/theme/fonts/. fonts/; fi; \
if [ -d /build/theme/images ]; then cp -r /build/theme/images/. images/; fi; \
for css in styles print embed; do \
lessc --clean-css="--s1 --advanced" styles/$css.less > styles/$css.min.css; \
done; \
ls -l styles/*.min.css
# --- Étage 2 : image finale ---
FROM upstream
ARG ASPELL_DICTS="fr en"
# Dictionnaires installés au build (plutôt qu'à chaque démarrage via ROUNDCUBEMAIL_ASPELL_DICTS)
# mailcap fournit /etc/mime.types, utilisé par Roundcube pour le mapping type MIME <-> extension
RUN --mount=type=secret,id=ca,required=false \
set -eu; \
if [ -s /run/secrets/ca ]; then export SSL_CERT_FILE=/run/secrets/ca; fi; \
pkgs="mailcap"; \
for d in $(echo "$ASPELL_DICTS" | tr ',' ' '); do pkgs="$pkgs aspell-$d"; done; \
apk add --no-cache $pkgs
COPY --from=theme /build/elastic/ /usr/src/roundcubemail/skins/elastic/
# Healthcheck php-fpm : réponse au ping FastCGI via le pool
COPY roundcube/zz-healthcheck.conf /usr/local/etc/php-fpm.d/zz-healthcheck.conf
+5
View File
@@ -0,0 +1,5 @@
; Expose /fpm-ping sur le pool www, interrogé par nginx (healthcheck de bout en bout)
[www]
ping.path = /fpm-ping
ping.response = pong
access.suppress_path[] = /fpm-ping
+19
View File
@@ -0,0 +1,19 @@
#!/bin/sh
# Importe une customisation Elastic (format roundcube-custom/vblog) dans theme/ et config/
# Usage : scripts/sync-theme.sh [dossier_source] (défaut : ../roundcube-custom/vblog)
set -eu
cd "$(dirname "$0")/.."
SRC=${1:-../roundcube-custom/vblog}
[ -d "$SRC" ] || { echo "Source introuvable : $SRC" >&2; exit 1; }
rm -rf theme && mkdir -p theme
for f in _variables.less _styles.less; do
if [ -f "$SRC/$f" ]; then cp "$SRC/$f" theme/; fi
done
for d in fonts images; do
if [ -d "$SRC/$d" ]; then cp -r "$SRC/$d" theme/; fi
done
if [ -f "$SRC/config-vblog.inc.php" ]; then
cp "$SRC/config-vblog.inc.php" config/20-vblog.inc.php
fi
echo "Thème importé depuis $SRC. Appliquer : docker compose up -d --build"
+75
View File
@@ -0,0 +1,75 @@
// Compléments CSS charte vBlog.io pour le skin Elastic (Roundcube)
// Emplacement : skins/elastic/styles/_styles.less
// --- Police Poppins auto-hébergée (pas d'appel à Google Fonts) ---
// Fichiers à copier dans skins/elastic/fonts/ (source : paquet npm @fontsource/poppins, licence OFL)
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('../fonts/poppins-latin-400-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Poppins';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url('../fonts/poppins-latin-400-italic.woff2') format('woff2');
}
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('../fonts/poppins-latin-700-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Poppins';
font-style: italic;
font-weight: 700;
font-display: swap;
src: url('../fonts/poppins-latin-700-italic.woff2') format('woff2');
}
body, button, input, optgroup, select, textarea, .popover {
font-family: Poppins, Roboto, sans-serif;
}
// --- Bouton principal de la page de login en orange (CTA du blog) ---
#login-form .btn-primary {
background-color: @vblog-accent;
border-color: @vblog-accent;
&:hover, &:focus {
background-color: darken(@vblog-accent, 8%);
border-color: darken(@vblog-accent, 8%);
}
}
// --- Mode sombre : liens lisibles sur fond sombre, bouton "Écrire" en orange ---
// :where() ramène la spécificité à celle de la règle de base "a { color: @color-link }"
// de global.less : seuls les liens "nus" changent, pas les listes ni les boutons.
:where(html.dark-mode) a {
color: lighten(@vblog-primary, 40%);
&:hover {
color: lighten(@vblog-primary, 50%);
}
}
html.dark-mode {
#taskmenu .action-buttons a {
color: @vblog-accent;
}
// Elastic assombrit le logo en mode sombre (opacity .8) : on le laisse net
#logo {
opacity: 1;
}
}
// --- Logo du menu latéral : l'emblème est presque carré, on lui laisse de l'air ---
#layout-menu .popover-header img {
padding: .6rem;
}
+31
View File
@@ -0,0 +1,31 @@
// Charte vBlog.io pour le skin Elastic (Roundcube)
// Emplacement : skins/elastic/styles/_variables.less
// Source des couleurs : kit Elementor de vblog.io
// https://vblog.io/wp-content/uploads/elementor/css/post-6.css (.elementor-kit-6)
// primary #314977 · secondary #000000 · text #7A7A7A · accent #FF7B10 · police Poppins
@vblog-primary: #314977; // bleu marine
@vblog-accent: #FF7B10; // orange
// --- Couleur principale (boutons, sélection, focus, cases à cocher, quota...) ---
@color-main: @vblog-primary;
@color-link: @vblog-primary;
@color-link-hover: darken(@color-link, 12%);
// --- Menu latéral gauche : fond marine, action "Écrire" en orange ---
@color-taskmenu-background: @vblog-primary;
@color-taskmenu-button-selected-background: darken(@vblog-primary, 8%);
@color-taskmenu-button-background-hover: darken(@vblog-primary, 8%);
@color-taskmenu-button-action: @vblog-accent;
@color-taskmenu-button-action-hover: lighten(@vblog-accent, 10%);
// --- Accent orange sur les badges des messages récents ---
@color-list-recent-badge-background: @vblog-accent;
@color-list-badge-background: @vblog-primary;
// --- Mode sombre : le marine est trop sombre sur fond sombre, on l'éclaircit ---
@color-dark-list-selected: lighten(@vblog-primary, 40%);
@color-dark-input-border-focus: lighten(@vblog-primary, 30%);
@color-dark-information: @vblog-primary;
@color-dark-scrollbar-thumb: lighten(@vblog-primary, 10%);
@color-dark-blockquote-0: lighten(@vblog-primary, 35%);
+93
View File
@@ -0,0 +1,93 @@
Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-ThinItalic.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-ExtraLight.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-ExtraLightItalic.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-Light.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-LightItalic.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-Regular.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-Italic.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-Medium.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-MediumItalic.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-SemiBold.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-SemiBoldItalic.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-Bold.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-BoldItalic.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-ExtraBold.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-ExtraBoldItalic.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-Black.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) Poppins-BlackItalic.ttf: Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2593.62 1936.98" xmlns:c2pa="http://c2pa.org/manifest"><metadata><c2pa:manifest>AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTpkYTI3NDZjNi1mNzFiLTRmYTktODExZS05M2NkYWY4NjhhNzQAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaA91XmY9vYc/lQWs5mc/nSgAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDo5NDg4N2FjYy0zZWRlLTRkNTEtYjEwNC1jNzI4ODJlYzJjODVscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNo0cSopkBTFmCEdCRaDm4D2QAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggHLDHh6IB6u0VT/kooPSrah5drQE82Y5rJoe/qvpz5TqkZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaMasuQR/8QV+DkHEEuN8x9AAAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCAT0buA0oWePCeILrY7wuF+ctrG5lgvGRcyRirNXcHZLGRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBjNZmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOmRhMjc0NmM2LWY3MWItNGZhOS04MTFlLTkzY2RhZjg2OGE3NC9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOmYwZWJlY2QzLWY1ZWItNGU1OS1hYzAwLWMyYWQ1OTM4MzAxZXJjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCAcsMeHogHq7RVP+Sig9KtqHl2tATzZjmsmh7+q+nPlOqJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFggANyWZH5B4dxS/hb406toS2r01DxDX6NJMAeOnX/BNKeiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggmc3m2Mzqiz5Id6nTzYaBsHtDc0/mJtVh/TILchty2U90Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALine truncated
<defs>
<style>
.cls-1 {
fill: #ffffff;
}
</style>
</defs>
<g id="_Слой_1" data-name="Слой 1">
<path class="cls-1" d="M403.71,122.09c-14.13-24.78-14.02-55.55.33-80.27,14.42-24.69,41.07-40.01,69.62-40.01h335c28.62,0,55.31,15.45,69.66,40.32l423.47,733.13,371.83-645.99c-1.32-6.84-1.98-13.4-1.98-19.86,0-67.97,61.31-122.42,133.2-106.66,9.37,1.08,19.66,4.54,28.95,9.96,5.01,2.82,10.14,6.7,15.26,11.7,25.73,19.51,41.44,51.03,41.44,85.01,0,49.58-33.31,92.57-80.38,105.42l-438.37,761.55c-14.35,24.87-41.07,40.32-69.77,40.32s-55.31-15.45-69.66-40.32L762.25,162.74h-150.37l710.12,1248.35c45.72,14.13,76.98,55.88,76.98,104.29,0,60.38-49.12,109.5-109.52,109.5s-109.37-49.12-109.37-109.5c0-7.52.95-15.12,2.93-23.43L403.71,122.09ZM2582.94,41.85c-14.62-24.74-41.23-39.73-69.72-39.73h-360.58c-28.86,0-55.85,15.37-70.1,40.48l-615.84,1072.01c-43.11,15.37-72.34,56.6-72.34,102.7,0,60.35,48.72,109.45,109.07,109.45s109.82-49.1,109.82-109.45c0-9-1.5-18.37-4.12-27.74L2199.12,162.92h175.42l-322.9,556.87c-43.7,14.03-75.53,54.51-75.53,102.86,0,59.93,48.59,108.52,108.52,108.52s108.52-48.59,108.52-108.52c0-7.74-.88-15.27-2.42-22.55l392.21-678.05c14.24-24.74,14.24-55.47,0-80.21ZM1917.3,1008.41c-59.94,0-108.52,48.59-108.52,108.52,0,9.53,1.62,18.61,3.93,27.42l-289.78,505.88c-45.35,79.09-126.7,125.94-217.78,125.94s-172.42-46.85-217.78-125.94L215.9,137.06c2.25-8.25,3-16.12,3-23.99,0-60.35-49.1-109.45-109.45-109.45S0,52.72,0,113.07c0,48.35,31.11,89.96,76.84,104.2l871.1,1513.18c74.22,129.31,207.66,206.53,357.21,206.53s283-77.21,357.21-206.9l293.29-511.94c40.93-15.53,70.17-54.82,70.17-101.2,0-59.93-48.59-108.52-108.52-108.52Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2593.62 1936.98" xmlns:c2pa="http://c2pa.org/manifest"><metadata><c2pa:manifest>AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTpmYTA2ZmM0My0yMGFkLTRmYzMtOTc4Zi00NDdmNWE1ZDNiNmEAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaFN7GjaL+DvcZ7qDIvssT+QAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDoyMTQxNjZhMS01OWQ5LTQ1MTAtYjdlNi1mNjc1NjM0YjNiYjhscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNotc0ool/uwYHS0xMCXfhROQAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFgg0gLQY2DQpyuLaKHGYmxL9pIGWRXgVZQndUlPC3BBZ/GkZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaCHV0xW1SMJqd+3ehqhKsZUAAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCCef7I8bNY42SEO0ST7/4olLBw27oaPO19GdQejLNz5HWRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBjNZmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOmZhMDZmYzQzLTIwYWQtNGZjMy05NzhmLTQ0N2Y1YTVkM2I2YS9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOjI4ZGFhZTE0LWNiNDgtNDNhOC05M2NkLTZjYjliN2ZjZjhjY3JjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCDSAtBjYNCnK4toocZibEv2kgZZFeBVlCd1SU8LcEFn8aJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFgg7mshbHpJrugvO3izq6qTUyqOAO23FFoU7v69VayZMmOiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggFBFyHyxF4DRsGQWlQaYvja1e/2VaVq3EX3/njPu9PpV0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALine truncated
<defs>
<style>
.cls-1 {
fill: #314977;
}
</style>
</defs>
<g id="_Слой_1" data-name="Слой 1">
<path class="cls-1" d="M403.71,122.09c-14.13-24.78-14.02-55.55.33-80.27,14.42-24.69,41.07-40.01,69.62-40.01h335c28.62,0,55.31,15.45,69.66,40.32l423.47,733.13,371.83-645.99c-1.32-6.84-1.98-13.4-1.98-19.86,0-67.97,61.31-122.42,133.2-106.66,9.37,1.08,19.66,4.54,28.95,9.96,5.01,2.82,10.14,6.7,15.26,11.7,25.73,19.51,41.44,51.03,41.44,85.01,0,49.58-33.31,92.57-80.38,105.42l-438.37,761.55c-14.35,24.87-41.07,40.32-69.77,40.32s-55.31-15.45-69.66-40.32L762.25,162.74h-150.37l710.12,1248.35c45.72,14.13,76.98,55.88,76.98,104.29,0,60.38-49.12,109.5-109.52,109.5s-109.37-49.12-109.37-109.5c0-7.52.95-15.12,2.93-23.43L403.71,122.09ZM2582.94,41.85c-14.62-24.74-41.23-39.73-69.72-39.73h-360.58c-28.86,0-55.85,15.37-70.1,40.48l-615.84,1072.01c-43.11,15.37-72.34,56.6-72.34,102.7,0,60.35,48.72,109.45,109.07,109.45s109.82-49.1,109.82-109.45c0-9-1.5-18.37-4.12-27.74L2199.12,162.92h175.42l-322.9,556.87c-43.7,14.03-75.53,54.51-75.53,102.86,0,59.93,48.59,108.52,108.52,108.52s108.52-48.59,108.52-108.52c0-7.74-.88-15.27-2.42-22.55l392.21-678.05c14.24-24.74,14.24-55.47,0-80.21ZM1917.3,1008.41c-59.94,0-108.52,48.59-108.52,108.52,0,9.53,1.62,18.61,3.93,27.42l-289.78,505.88c-45.35,79.09-126.7,125.94-217.78,125.94s-172.42-46.85-217.78-125.94L215.9,137.06c2.25-8.25,3-16.12,3-23.99,0-60.35-49.1-109.45-109.45-109.45S0,52.72,0,113.07c0,48.35,31.11,89.96,76.84,104.2l871.1,1513.18c74.22,129.31,207.66,206.53,357.21,206.53s283-77.21,357.21-206.9l293.29-511.94c40.93-15.53,70.17-54.82,70.17-101.2,0-59.93-48.59-108.52-108.52-108.52Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB