Vogelalbum 1.0.0
Eine Kamera am Vogelhaus, ein Rechner der erkennt wer da war — und eine Sammlung, die sich von allein fuellt. Zwei neuronale Netze: eines findet den Vogel im Bild, eines bestimmt die Art. 148 heimische Arten, auf Deutschland eingeschraenkt und neu normiert — ohne diese Einschraenkung wird aus einer Kohlmeise eine nordamerikanische Schwesterart. Daraus entsteht ein Sammelalbum: nummerierte Seiten, gefundene Arten in Farbe, fehlende als Schatten, dazu Erstfang, Punkte, Stufen und 15 Abzeichen. Laeuft vollstaendig auf eigener Hardware. Kein Cloud-Dienst, kein Konto, keine Telemetrie; die Bilder verlassen das eigene Netz nicht. Zwei getrennt deploybare Teile: die Sammlung als Django-Anwendung, die Erkennung als eigener Dienst. Der Erkennungs-Dienst fragt selbst an und braucht deshalb weder eine erreichbare Adresse noch einen offenen Port — er laeuft hinter einem gewoehnlichen Router. Dabei: Firmware fuer Raspberry Pi und ESP32-S3, Anbindung an Home Assistant ueber MQTT, Oberflaeche auf Deutsch und Englisch, Einstellungen je Kamera aus dem Browser, und ein Weg fuer Fotos von unterwegs. AGPL-3.0.
No files matched your search
@@ -0,0 +1,781 @@
|
||||
/* Vogelalbum — eigenständiges Stylesheet, kein Build-Schritt.
|
||||
*
|
||||
* Zwei Modi über Farbtokens: jede Farbe existiert einmal hell und einmal
|
||||
* dunkel, umgeschaltet wird nur die Zuordnung. Deshalb steht unten kein
|
||||
* einziger Farbwert mehr direkt in einer Regel.
|
||||
*
|
||||
* Die Akzentfarbe kommt vom Typ der Art, nicht global — dadurch fühlt sich
|
||||
* jede Artseite anders an. Die typabhängigen Töne werden in
|
||||
* dex/species_meta.py vorberechnet und als Inline-Variablen mitgegeben, in
|
||||
* beiden Modi (Endung -l und -d). Die Indirektion weiter unten wählt aus.
|
||||
*
|
||||
* Keine modernen Funktionen ohne Not: kein color-mix (Safari ab 16.2), kein
|
||||
* aspect-ratio (Safari ab 15). Beides fällt auf älteren Geräten ersatzlos weg
|
||||
* und reißt das Layout auseinander — Bildverhältnisse laufen über padding-top.
|
||||
*/
|
||||
|
||||
/* Die Palette kommt aus der Marke: tiefes Grün und Creme. Vorher war das
|
||||
* Grün der Oberfläche ein anderes als das des Zeichens — nebeneinander sah
|
||||
* man den Unterschied sofort, und die Seite wirkte kühl und beliebig.
|
||||
* Papierweiß statt Blauweiß, damit Fotos von Vögeln warm wirken. */
|
||||
:root {
|
||||
--bg: #fbf8ef;
|
||||
--bg-grad: #f4eddd;
|
||||
--surface: #ffffff;
|
||||
--surface-2: #f3ece0;
|
||||
--line: #e7dfcc;
|
||||
--text: #14281c;
|
||||
--text-2: #5c6b60;
|
||||
--muted: #7a8a7f;
|
||||
|
||||
--accent: #0f5f3c;
|
||||
--accent-2: #0a3f28;
|
||||
--accent-wash: #e6f0e9;
|
||||
--accent-on: #fcf2d3;
|
||||
|
||||
/* Der Kopf ist dunkel, damit die Seite nicht als weißes Blatt beginnt. */
|
||||
--kopf-bg: #0a3f28;
|
||||
--kopf-text: #fcf2d3;
|
||||
--kopf-text-2: #a8ceb8;
|
||||
--kopf-an: rgba(255,255,255,.15);
|
||||
--kopf-linie: rgba(255,255,255,.22);
|
||||
--gold: #cf9a1c;
|
||||
--gold-wash: #fbf3dc;
|
||||
--gold-line: #ecd9a0;
|
||||
|
||||
--shadow: 0 1px 2px rgba(22,33,26,.05), 0 8px 20px -12px rgba(22,33,26,.18);
|
||||
--shadow-lift: 0 2px 4px rgba(22,33,26,.07), 0 16px 32px -16px rgba(22,33,26,.26);
|
||||
--nr-bg: rgba(255,255,255,.92);
|
||||
--head-bg: rgba(255,255,255,.86);
|
||||
--locked-opacity: .30;
|
||||
|
||||
--r-sm: 10px;
|
||||
--r: 16px;
|
||||
--r-lg: 24px;
|
||||
--font: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
||||
|
||||
--typ: #7f9b6a;
|
||||
--typ-ink: #ffffff;
|
||||
}
|
||||
|
||||
/* Typabhängige Töne werden je Modus direkt am Verwendungsort gewählt.
|
||||
Der naheliegende Umweg über eine Variable auf :root funktioniert nicht:
|
||||
var() in einer Custom-Property-Deklaration wird auf dem deklarierenden
|
||||
Element ersetzt, und dort gibt es die -l/-d-Werte der Karte nicht. */
|
||||
|
||||
[data-theme="dark"] {
|
||||
/* Kühles Blaugrau statt Dunkelgrün: neutral genug, dass 16 Typfarben
|
||||
darauf nebeneinander funktionieren, ohne sich mit dem Grund zu beißen. */
|
||||
--bg: #10151b;
|
||||
--bg-grad: #141b23;
|
||||
--surface: #1a2129;
|
||||
--surface-2: #222b35;
|
||||
--line: #2e3945;
|
||||
--text: #e8eef4;
|
||||
--text-2: #b3c1cd;
|
||||
--muted: #7e8f9e;
|
||||
|
||||
--accent: #45c98a;
|
||||
--accent-2: #2f9d68;
|
||||
--accent-wash: #163024;
|
||||
--accent-on: #08150d;
|
||||
--gold: #e8be5a;
|
||||
--gold-wash: #2a2415;
|
||||
--gold-line: #4a4026;
|
||||
|
||||
/* Auf dunklem Grund trägt nicht der Schatten, sondern die Kante. */
|
||||
--shadow: 0 1px 2px rgba(0,0,0,.4);
|
||||
--shadow-lift: 0 10px 30px -14px rgba(0,0,0,.75);
|
||||
--nr-bg: rgba(10,14,19,.82);
|
||||
--head-bg: rgba(16,21,27,.86);
|
||||
--locked-opacity: .26;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--font);
|
||||
color: var(--text);
|
||||
background: linear-gradient(180deg, var(--bg-grad) 0%, var(--bg) 420px);
|
||||
background-attachment: fixed;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
a { color: inherit; text-decoration: none; }
|
||||
img { max-width: 100%; }
|
||||
|
||||
/* ⚠ padding-inline, nicht die Kurzform ``padding``. Der Kopf traegt
|
||||
``class="wrap top-inner"``, und ``.top-inner`` setzt den senkrechten
|
||||
Abstand (12px). Eine Kurzform hier setzt den auf 0 zurueck — genau das
|
||||
ist auf Telefonen passiert: Marke und Navigation klebten oben und unten
|
||||
an der Kante der gruenen Leiste. */
|
||||
.wrap { max-width: 1120px; margin: 0 auto; padding-inline: 20px; }
|
||||
|
||||
/* ---------- Kopf ---------- */
|
||||
|
||||
/* 🔑 Der Kopf ist dunkel, nicht durchscheinend hell. Die Seite begann vorher
|
||||
als weisses Blatt und wirkte dadurch unfertig; eine dunkle Leiste gibt ihr
|
||||
oben einen Halt und laesst die Fotos darunter waermer wirken. */
|
||||
header.top {
|
||||
position: sticky; top: 0; z-index: 30;
|
||||
background: var(--kopf-bg);
|
||||
color: var(--kopf-text);
|
||||
}
|
||||
|
||||
.top-inner { display: flex; align-items: center; gap: 14px; padding: 12px 0; flex-wrap: wrap; }
|
||||
|
||||
.brand { font-weight: 800; font-size: 21px; letter-spacing: -0.03em; display: flex; align-items: center; gap: 10px; color: var(--kopf-text); }
|
||||
/* 🔑 Die Wortmarke ist EIN Flex-Kind, nicht zwei.
|
||||
Vorher standen "Vogel" und "album" getrennt im Flex-Container und bekamen
|
||||
dessen gap von 10 px zwischen sich — bei "Bird dex" war das gewollt, bei
|
||||
einem deutschen Kompositum liest es sich wie ein Tippfehler. Jetzt trennt
|
||||
der Abstand nur noch Zeichen und Wort; die Farbe wechselt mitten im Wort. */
|
||||
.brand .wort { color: var(--kopf-text); }
|
||||
.brand .wort span { color: #7fd3a4; }
|
||||
|
||||
/* ⚠ Das Zeichen traegt dunkelgruene Flaechen. Direkt auf der dunklen Leiste
|
||||
verschwinden die, und uebrig bleibt ein cremefarbener Klecks — die Marke
|
||||
war an der wichtigsten Stelle nicht mehr zu erkennen. Deshalb eine helle
|
||||
Scheibe darunter: das Zeichen selbst bleibt unveraendert, es bekommt nur
|
||||
einen Traeger. Nebenbei nimmt die runde Form die Abzeichen-Pins auf. */
|
||||
.brand .scheibe {
|
||||
width: 40px; height: 40px; flex: 0 0 40px;
|
||||
border-radius: 50%; background: var(--kopf-text);
|
||||
display: grid; place-items: center;
|
||||
}
|
||||
.brand img { display: block; width: 31px; height: 31px; }
|
||||
|
||||
nav.main { display: flex; gap: 3px; margin-left: auto; flex-wrap: wrap; }
|
||||
nav.main a {
|
||||
padding: 8px 15px; border-radius: 999px;
|
||||
font-size: 14px; font-weight: 600; color: var(--text-2);
|
||||
transition: background .15s, color .15s;
|
||||
}
|
||||
nav.main a:hover { background: var(--surface-2); color: var(--text); }
|
||||
nav.main a.active { background: var(--accent); color: var(--accent-on); }
|
||||
|
||||
/* ---------- Bedienelemente auf der dunklen Leiste ----------
|
||||
Alles im Kopf steht jetzt auf Dunkelgruen. Die Regeln davor rechneten mit
|
||||
hellem Grund; ohne diesen Block waere die Navigation dunkelgrau auf
|
||||
dunkelgruen, also praktisch unsichtbar. Gesammelt an einer Stelle, damit
|
||||
beim naechsten Farbwechsel klar ist, was zusammengehoert. */
|
||||
header.top nav.main a { color: var(--kopf-text-2); }
|
||||
header.top nav.main a:hover { background: rgba(255,255,255,.10); color: var(--kopf-text); }
|
||||
header.top nav.main a.active { background: var(--kopf-an); color: var(--kopf-text); }
|
||||
|
||||
header.top .live-knopf {
|
||||
background: transparent; border-color: var(--kopf-linie); color: var(--kopf-text-2);
|
||||
}
|
||||
header.top .live-knopf:hover { color: var(--kopf-text); border-color: var(--kopf-text); }
|
||||
|
||||
header.top .lang-switch { background: transparent; border-color: var(--kopf-linie); }
|
||||
header.top .lang-switch button { color: var(--kopf-text-2); }
|
||||
header.top .lang-switch button:hover { color: var(--kopf-text); }
|
||||
header.top .lang-switch button.on { background: var(--kopf-text); color: var(--kopf-bg); }
|
||||
|
||||
header.top .theme-toggle, header.top .gear {
|
||||
background: transparent; border-color: var(--kopf-linie); color: var(--kopf-text-2);
|
||||
}
|
||||
header.top .theme-toggle:hover, header.top .gear:hover {
|
||||
border-color: var(--kopf-text); color: var(--kopf-text);
|
||||
}
|
||||
|
||||
/* Der Live-Knopf. Kein Navigationspunkt, sondern ein Hinweis, dass gerade
|
||||
etwas zu sehen ist — der Punkt pulst, solange die Seite offen ist. Rot ist
|
||||
hier die richtige Farbe: sie heisst "laeuft jetzt", nicht "Fehler". */
|
||||
.live-knopf {
|
||||
flex: none; display: inline-flex; align-items: center; gap: 6px;
|
||||
font-size: 13px; font-weight: 700; text-decoration: none;
|
||||
color: var(--text-2); background: var(--surface);
|
||||
border: 1px solid var(--line); border-radius: 999px; padding: 6px 12px 6px 10px;
|
||||
transition: background .15s, color .15s, border-color .15s;
|
||||
}
|
||||
.live-knopf:hover { color: var(--text); border-color: var(--text-2); }
|
||||
.live-knopf.on { background: #d6303a; border-color: #d6303a; color: #fff; }
|
||||
.live-knopf .punkt {
|
||||
width: 8px; height: 8px; border-radius: 50%; background: #d6303a;
|
||||
animation: live-puls 2s ease-in-out infinite;
|
||||
}
|
||||
.live-knopf.on .punkt { background: #fff; }
|
||||
|
||||
@keyframes live-puls {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: .35; }
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
width: 38px; height: 38px; flex-shrink: 0;
|
||||
border: 1px solid var(--line); border-radius: 50%;
|
||||
background: var(--surface); color: var(--text-2);
|
||||
font-size: 17px; line-height: 1; cursor: pointer;
|
||||
display: grid; place-items: center;
|
||||
transition: border-color .15s, color .15s;
|
||||
}
|
||||
.theme-toggle:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.theme-toggle .icon-dark { display: none; }
|
||||
[data-theme="dark"] .theme-toggle .icon-dark { display: block; }
|
||||
[data-theme="dark"] .theme-toggle .icon-light { display: none; }
|
||||
|
||||
/* ---------- Fortschritt ---------- */
|
||||
|
||||
.progress-bar {
|
||||
display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
|
||||
padding: 18px 22px; margin: 22px 0;
|
||||
border-radius: var(--r-lg);
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.level-badge {
|
||||
width: 60px; height: 60px; border-radius: 50%;
|
||||
display: grid; place-items: center; flex-shrink: 0;
|
||||
font-weight: 800; font-size: 24px; color: var(--accent-on);
|
||||
background: linear-gradient(145deg, var(--accent), var(--accent-2));
|
||||
box-shadow: 0 0 0 5px var(--accent-wash);
|
||||
}
|
||||
|
||||
.xp-track { height: 11px; border-radius: 999px; overflow: hidden; background: var(--surface-2); }
|
||||
.xp-fill {
|
||||
height: 100%; border-radius: 999px;
|
||||
background: linear-gradient(90deg, var(--accent-2), var(--accent));
|
||||
transition: width .6s cubic-bezier(.2,.8,.3,1);
|
||||
}
|
||||
|
||||
.rank { font-weight: 750; font-size: 18px; letter-spacing: -0.015em; }
|
||||
.sub { color: var(--muted); font-size: 13px; }
|
||||
|
||||
/* ---------- Kennzahlen ---------- */
|
||||
|
||||
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px,1fr)); gap: 12px; }
|
||||
.stat {
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
border-radius: var(--r); padding: 17px 19px; box-shadow: var(--shadow);
|
||||
}
|
||||
.stat .n { font-size: 30px; font-weight: 800; letter-spacing: -0.03em; line-height: 1; color: var(--accent-2); }
|
||||
.stat .l { color: var(--muted); font-size: 13px; margin-top: 6px; }
|
||||
|
||||
h2 {
|
||||
font-size: 21px; margin: 36px 0 15px; letter-spacing: -0.025em;
|
||||
display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
|
||||
}
|
||||
h2 .count { color: var(--muted); font-weight: 500; font-size: 14px; letter-spacing: 0; }
|
||||
|
||||
/* ---------- Sammelkarten ---------- */
|
||||
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px,1fr)); gap: 16px; }
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
border-radius: var(--r); overflow: hidden;
|
||||
display: flex; flex-direction: column;
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
box-shadow: var(--shadow);
|
||||
transition: transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .18s, border-color .18s;
|
||||
}
|
||||
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lift); border-color: var(--typ-line-l, var(--line)); }
|
||||
[data-theme="dark"] .card:hover { border-color: var(--typ-line-d, var(--line)); }
|
||||
|
||||
/* Farbband oben — die Karte bekennt ihren Typ, bevor man den Namen liest. */
|
||||
.card::before {
|
||||
content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
|
||||
background: var(--typ); z-index: 3;
|
||||
}
|
||||
|
||||
.card .nr {
|
||||
position: absolute; top: 12px; left: 11px; z-index: 2;
|
||||
font-size: 11px; font-weight: 800; letter-spacing: .05em;
|
||||
padding: 3px 8px; border-radius: 999px;
|
||||
background: var(--nr-bg); color: var(--text-2);
|
||||
}
|
||||
|
||||
.card .thumb {
|
||||
position: relative; width: 100%;
|
||||
padding-top: 75%; /* 4:3, funktioniert ohne aspect-ratio */
|
||||
background: var(--typ-wash-l, var(--surface-2));
|
||||
overflow: hidden;
|
||||
}
|
||||
[data-theme="dark"] .card .thumb { background: var(--typ-wash-d, var(--surface-2)); }
|
||||
.card .thumb img {
|
||||
position: absolute; inset: 0;
|
||||
width: 100%; height: 100%; object-fit: cover; display: block;
|
||||
}
|
||||
|
||||
/* Nicht gesammelt: entfärbt und blass. Die Form ist zu ahnen, die
|
||||
Federzeichnung nicht — der Reiz des Freischaltens bleibt, ohne dass die
|
||||
Karte tot wirkt. */
|
||||
/* ⚠ Hier stand der Typton der Art als Grund. Gemeint war ein Hauch Farbe,
|
||||
wirksam war ein Flickenteppich: nebeneinander lagen graue, sepiafarbene,
|
||||
rosa und violette Kacheln, und das sah nach Zufall aus statt nach System.
|
||||
Verschlossen ist ein Zustand, kein Thema — also fuer alle derselbe
|
||||
neutrale Grund. Die Typfarbe bleibt sichtbar, wo sie etwas bedeutet:
|
||||
im Band oben und in der Pille unter dem Namen. */
|
||||
.card.locked .thumb { background: var(--surface-2); }
|
||||
[data-theme="dark"] .card.locked .thumb { background: var(--surface-2); }
|
||||
.card.locked .thumb img { filter: grayscale(1) contrast(.85); opacity: var(--locked-opacity); }
|
||||
.card.locked .thumb::after {
|
||||
content: "?"; position: absolute;
|
||||
right: 12px; bottom: 6px;
|
||||
font-size: 34px; font-weight: 800; line-height: 1;
|
||||
color: var(--typ); opacity: .6;
|
||||
}
|
||||
.card.locked .name { color: var(--text-2); }
|
||||
|
||||
.card .body { padding: 12px 14px 14px; }
|
||||
.card .name { font-weight: 700; font-size: 15.5px; line-height: 1.25; letter-spacing: -0.015em; }
|
||||
.card .meta { display: flex; align-items: center; gap: 6px; margin-top: 9px; flex-wrap: wrap; }
|
||||
.card .times { margin-left: auto; color: var(--muted); font-size: 12px; font-weight: 700; }
|
||||
|
||||
/* ---------- Badges ---------- */
|
||||
|
||||
.pill {
|
||||
font-size: 11px; font-weight: 800; letter-spacing: .03em;
|
||||
padding: 4px 10px; border-radius: 999px;
|
||||
color: var(--typ-ink); background: var(--typ);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.pill.ghost { background: var(--surface-2); color: var(--text-2); border: 1px solid var(--line); }
|
||||
.pill.rar { color: #fff; }
|
||||
|
||||
.tag-new {
|
||||
background: var(--gold); color: #fff;
|
||||
font-size: 10px; font-weight: 800; letter-spacing: .06em;
|
||||
padding: 3px 8px; border-radius: 999px; text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* ---------- Artseite ---------- */
|
||||
|
||||
.species-hero {
|
||||
position: relative; overflow: hidden;
|
||||
border-radius: var(--r-lg); margin: 18px 0 8px;
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
/* Kräftiges Typband am oberen Rand statt einer flächigen Einfärbung — das
|
||||
gibt der Seite ihre Identität, ohne den Text zu bedrängen. */
|
||||
.species-hero::before {
|
||||
content: ""; position: absolute; top: 0; left: 0; right: 0; height: 6px;
|
||||
background: var(--typ);
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
display: grid; grid-template-columns: minmax(0,1.05fr) minmax(0,1fr);
|
||||
gap: 26px; padding: 28px 26px 26px;
|
||||
}
|
||||
@media (max-width: 950px) { .hero-grid { grid-template-columns: 1fr; padding: 22px 18px 18px; } }
|
||||
|
||||
.hero-photo {
|
||||
position: relative; width: 100%; padding-top: 75%;
|
||||
border-radius: var(--r); overflow: hidden;
|
||||
background: var(--typ-wash-l, var(--surface-2)); border: 1px solid var(--typ-line-l, var(--line));
|
||||
}
|
||||
[data-theme="dark"] .hero-photo { background: var(--typ-wash-d, var(--surface-2)); border-color: var(--typ-line-d, var(--line)); }
|
||||
.hero-photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
|
||||
.dexnr { font-size: 13px; font-weight: 800; letter-spacing: .12em; color: var(--typ-text-l, var(--muted)); }
|
||||
[data-theme="dark"] .dexnr { color: var(--typ-text-d, var(--muted)); }
|
||||
.species-hero h1 { margin: 4px 0 2px; font-size: clamp(30px, 5vw, 44px); letter-spacing: -0.04em; line-height: 1.02; }
|
||||
.kategorie { font-size: 16px; font-weight: 700; color: var(--typ-text-l, var(--text-2)); margin-bottom: 5px; }
|
||||
[data-theme="dark"] .kategorie { color: var(--typ-text-d, var(--text-2)); }
|
||||
.sci { color: var(--muted); font-style: italic; font-size: 14px; }
|
||||
|
||||
.badge-row { display: flex; gap: 7px; flex-wrap: wrap; margin: 16px 0; }
|
||||
|
||||
/* Statuswerte als Balken — die Geste jeder Sammelkarte. */
|
||||
.werte { display: grid; gap: 9px; margin-top: 18px; }
|
||||
.wert { display: grid; grid-template-columns: 84px 1fr auto; align-items: center; gap: 11px; }
|
||||
.wert .k { font-size: 12px; font-weight: 700; color: var(--muted); }
|
||||
.wert .track { height: 9px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
|
||||
.wert .fill { height: 100%; border-radius: 999px; background: var(--typ); }
|
||||
.wert .v { font-size: 12.5px; font-weight: 750; font-variant-numeric: tabular-nums; min-width: 62px; text-align: right; }
|
||||
|
||||
/* ---------- Fließtext und Steckbrief ---------- */
|
||||
|
||||
.prose { max-width: 68ch; }
|
||||
.prose p { line-height: 1.7; margin: 0 0 16px; font-size: 16px; color: var(--text-2); }
|
||||
.prose p:first-of-type::first-letter {
|
||||
font-size: 3.1em; float: left; line-height: .82;
|
||||
padding: 5px 10px 0 0; font-weight: 800; color: var(--typ-text-l, var(--text-2));
|
||||
}
|
||||
[data-theme="dark"] .prose p:first-of-type::first-letter { color: var(--typ-text-d, var(--text-2)); }
|
||||
|
||||
.fakten { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px,1fr)); gap: 12px; margin: 18px 0; }
|
||||
.fakt {
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
border-left: 3px solid var(--typ);
|
||||
border-radius: var(--r); padding: 14px 16px; box-shadow: var(--shadow);
|
||||
}
|
||||
.fakt .k { font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
|
||||
.fakt .v { margin-top: 5px; line-height: 1.5; font-size: 14.5px; color: var(--text-2); }
|
||||
|
||||
.verwechslung { display: flex; gap: 12px; flex-wrap: wrap; }
|
||||
.vw {
|
||||
display: flex; align-items: center; gap: 11px;
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
border-radius: 999px; padding: 6px 18px 6px 6px;
|
||||
box-shadow: var(--shadow);
|
||||
transition: transform .15s, box-shadow .15s;
|
||||
}
|
||||
.vw:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
|
||||
.vw img { width: 42px; height: 42px; border-radius: 50%; object-fit: cover; background: var(--surface-2); }
|
||||
.vw .n { font-weight: 650; font-size: 14.5px; }
|
||||
|
||||
/* ---------- Sichtungen ---------- */
|
||||
|
||||
.rows { display: flex; flex-direction: column; gap: 9px; }
|
||||
.row {
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
border-radius: var(--r); padding: 10px 16px 10px 10px;
|
||||
box-shadow: var(--shadow); transition: box-shadow .15s;
|
||||
}
|
||||
.row:hover { box-shadow: var(--shadow-lift); }
|
||||
.row img.shot, .row .shot {
|
||||
width: 74px; height: 56px; object-fit: cover;
|
||||
border-radius: var(--r-sm); background: var(--surface-2); flex-shrink: 0;
|
||||
}
|
||||
.row .who { font-weight: 680; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.row .when { color: var(--muted); font-size: 13px; margin-top: 2px; }
|
||||
.row .right { margin-left: auto; text-align: right; flex-shrink: 0; }
|
||||
.row .conf { font-variant-numeric: tabular-nums; font-weight: 800; font-size: 15px; }
|
||||
.row .xp { color: var(--accent-2); font-size: 13px; font-weight: 800; }
|
||||
|
||||
/* ---------- Abzeichen ---------- */
|
||||
|
||||
.badges { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px,1fr)); gap: 13px; }
|
||||
.badge {
|
||||
display: flex; gap: 14px; align-items: flex-start;
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
border-radius: var(--r); padding: 16px 18px; box-shadow: var(--shadow);
|
||||
}
|
||||
.badge.locked { opacity: .55; box-shadow: none; background: var(--surface-2); }
|
||||
/* Der Pin ist ein Bild, das Emoji ein Zeichen — beide landen in .em, deshalb
|
||||
gilt die Graustufe fuer beides. Fehlt der Pin, faellt die Vorlage aufs
|
||||
Emoji zurueck und die Karte sieht trotzdem richtig aus. */
|
||||
.badge .em { font-size: 30px; line-height: 1; filter: grayscale(1); }
|
||||
.badge img.em {
|
||||
width: 64px; height: 64px; flex: 0 0 64px; object-fit: contain;
|
||||
/* blass, solange es nicht verdient ist: man soll sehen, was es zu holen
|
||||
gibt, ohne dass es wie eingeloest aussieht. */
|
||||
opacity: .45;
|
||||
}
|
||||
.badge:not(.locked) .em { filter: none; }
|
||||
.badge:not(.locked) img.em {
|
||||
opacity: 1;
|
||||
filter: drop-shadow(0 3px 6px rgba(20,40,28,.28));
|
||||
}
|
||||
.badge:not(.locked) { border-color: var(--gold-line); background: var(--gold-wash); }
|
||||
.badge .n { font-weight: 750; font-size: 15.5px; }
|
||||
.badge .d { color: var(--muted); font-size: 13.5px; margin-top: 4px; line-height: 1.45; }
|
||||
|
||||
/* ---------- Aktivität über den Tag ---------- */
|
||||
|
||||
.hours { display: flex; align-items: flex-end; gap: 3px; height: 74px; margin-top: 12px; }
|
||||
.hours div { flex: 1; min-height: 3px; border-radius: 3px 3px 0 0; background: var(--typ); }
|
||||
.hours-axis { display: flex; justify-content: space-between; color: var(--muted); font-size: 11px; margin-top: 6px; }
|
||||
|
||||
/* ---------- Kleinteile ---------- */
|
||||
|
||||
.empty {
|
||||
border: 1px dashed var(--line); border-radius: var(--r-lg);
|
||||
padding: 42px 24px; text-align: center; color: var(--muted);
|
||||
line-height: 1.6; background: var(--surface);
|
||||
}
|
||||
|
||||
.filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
|
||||
.filters a {
|
||||
padding: 7px 14px; border-radius: 999px; font-size: 13px; font-weight: 650;
|
||||
border: 1px solid var(--line); background: var(--surface); color: var(--text-2);
|
||||
transition: background .15s, color .15s, border-color .15s;
|
||||
}
|
||||
.filters a:hover { border-color: var(--muted); }
|
||||
.filters a.on { background: var(--accent); color: var(--accent-on); border-color: var(--accent); }
|
||||
|
||||
.pager { display: flex; gap: 10px; justify-content: center; margin: 28px 0; }
|
||||
.pager a {
|
||||
padding: 9px 20px; border-radius: 999px; border: 1px solid var(--line);
|
||||
background: var(--surface); font-size: 14px; font-weight: 650; color: var(--text-2);
|
||||
}
|
||||
.pager a:hover { border-color: var(--accent); color: var(--accent); }
|
||||
|
||||
.blaettern { display: flex; justify-content: space-between; gap: 12px; margin: 20px 0 4px; }
|
||||
.blaettern a { color: var(--muted); font-size: 14px; font-weight: 650; }
|
||||
.blaettern a:hover { color: var(--text); }
|
||||
|
||||
footer.bottom {
|
||||
border-top: 1px solid var(--line); margin-top: 60px; padding: 24px 0 32px;
|
||||
color: var(--muted); font-size: 13px; line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Der Werbeblock im Fuss.
|
||||
Hier unten ist der richtige Ort: Wer so weit gescrollt hat, hat fremde
|
||||
Vogelfotos gesehen und verstanden, was das Ding tut. Erst dann traegt der
|
||||
Satz "kannst du auch haben". Deshalb ein Angebot mit Knopf statt einer
|
||||
Fussnote — die leise Fassung stand hier einen Commit lang und war genau
|
||||
das Gegenteil dessen, was die Seite leisten soll. */
|
||||
footer.bottom .werbung {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
gap: 28px; flex-wrap: wrap;
|
||||
background: var(--accent-wash); border: 1px solid var(--line);
|
||||
border-radius: 16px; padding: 22px 26px;
|
||||
/* Kein margin-bottom mehr: Unter dem Block kommt nichts, seit die zweite
|
||||
Fusszeile weg ist. Der Abstand nach unten gehoert dem footer selbst. */
|
||||
}
|
||||
footer.bottom .werbung-text { flex: 1 1 340px; }
|
||||
footer.bottom .werbung-haken {
|
||||
margin: 0 0 4px; font-size: 19px; font-weight: 800;
|
||||
letter-spacing: -0.02em; color: var(--text);
|
||||
}
|
||||
footer.bottom .werbung-satz { margin: 0; font-size: 14px; color: var(--text-2); max-width: 54ch; }
|
||||
footer.bottom .werbung-knopf {
|
||||
flex: none; display: inline-flex; align-items: center; gap: 8px;
|
||||
background: var(--accent); color: var(--accent-on);
|
||||
font-size: 15px; font-weight: 700; text-decoration: none;
|
||||
border-radius: 999px; padding: 12px 20px;
|
||||
transition: background .15s, transform .15s;
|
||||
}
|
||||
footer.bottom .werbung-knopf:hover { background: var(--accent-2); transform: translateY(-1px); }
|
||||
footer.bottom .werbung-knopf svg { transition: transform .15s; }
|
||||
footer.bottom .werbung-knopf:hover svg { transform: translateX(3px); }
|
||||
|
||||
/* Auf dem Telefon untereinander, der Knopf ueber die volle Breite — ein
|
||||
Knopf, den man mit dem Daumen trifft, ist mehr wert als einer, der
|
||||
huebsch rechts sitzt. */
|
||||
@media (max-width: 560px) {
|
||||
footer.bottom .werbung { padding: 20px; gap: 18px; }
|
||||
footer.bottom .werbung-knopf { width: 100%; justify-content: center; }
|
||||
}
|
||||
|
||||
/* Das Zahnrad zum Admin — gleiche Form wie der Modus-Umschalter, weil es
|
||||
dieselbe Sorte Knopf ist: eine Einstellung, kein Navigationspunkt. Oben bei
|
||||
den Bedienelementen versteht man es ohne Beschriftung; im Footer neben
|
||||
einem Fliesstext nicht, das war der erste Versuch. */
|
||||
.gear {
|
||||
width: 38px; height: 38px; flex-shrink: 0;
|
||||
border: 1px solid var(--line); border-radius: 50%;
|
||||
background: var(--surface); color: var(--text-2);
|
||||
font-size: 17px; line-height: 1;
|
||||
display: grid; place-items: center;
|
||||
transition: border-color .15s, color .15s;
|
||||
}
|
||||
.gear:hover { border-color: var(--accent); color: var(--accent); }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
|
||||
|
||||
/* ---------- Schmale Geräte ---------- */
|
||||
|
||||
/* Der Kopf bricht um, sobald Marke, Live-Knopf, Navigation, Sprache und
|
||||
Umschalter nicht mehr in eine Zeile passen. Gemessen, nicht geschätzt: bei
|
||||
810 px (iPad hochkant) rutschte sonst der Umschalter allein in Zeile zwei —
|
||||
einer der Gründe, warum Layoutarbeit in vier Breiten angesehen wird.
|
||||
Marke und die kleinen Knöpfe bleiben oben, die Navigation nimmt die Zeile
|
||||
darunter. */
|
||||
@media (max-width: 1000px) {
|
||||
.brand { order: 0; flex: 1 1 auto; }
|
||||
.live-knopf, .lang-switch, .theme-toggle, .gear { order: 1; }
|
||||
nav.main { order: 2; flex: 1 0 100%; margin-left: 0; }
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
/* Fünf Punkte passen auf einem Telefon nicht mehr in eine Zeile, und einer
|
||||
allein in der zweiten sieht nach Versehen aus. Also seitlich wischen
|
||||
statt umbrechen — die Zeile bleibt eine Zeile, egal wie viele Punkte
|
||||
dazukommen oder wie lang die Sprache sie macht ("Sightings" gegen
|
||||
"Sichtungen"). Die Leiste wird bündig am Rand gescrollt, damit man ihr
|
||||
ansieht, dass da noch etwas kommt. */
|
||||
nav.main {
|
||||
order: 2; flex: 1 0 100%; margin-left: 0;
|
||||
flex-wrap: nowrap; justify-content: flex-start;
|
||||
overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
/* Der Wisch-Rand gehört in die Leiste, nicht neben sie. */
|
||||
margin-inline: -14px; padding-inline: 14px;
|
||||
}
|
||||
nav.main::-webkit-scrollbar { display: none; }
|
||||
nav.main { gap: 1px; }
|
||||
nav.main a { padding: 7px 8px; font-size: 12.5px; white-space: nowrap; flex: none; }
|
||||
|
||||
.wrap { padding-inline: 14px; }
|
||||
.grid { grid-template-columns: repeat(auto-fill, minmax(150px,1fr)); gap: 12px; }
|
||||
|
||||
/* Auf 360-px-Geräten ergeben Marke, Live, Sprache und Umschalter zusammen
|
||||
380 px und der Umschalter faellt in eine dritte Zeile. Gemessen, nicht
|
||||
geraten: 154 + 68 + 90 + 38 plus Abstaende gegen 332 px Platz, und das
|
||||
Zahnrad kommt noch dazu. Also alles eine Stufe kompakter — und vom
|
||||
Sprachumschalter bleibt nur die Sprache, in die man wechseln kann: welche
|
||||
gerade aktiv ist, sieht man am Text der Seite. */
|
||||
.top-inner { gap: 8px; }
|
||||
.brand { font-size: 19px; }
|
||||
.live-knopf { font-size: 12px; padding: 5px 10px 5px 8px; gap: 5px; }
|
||||
.theme-toggle, .gear { width: 34px; height: 34px; font-size: 15px; }
|
||||
.lang-switch button.on { display: none; }
|
||||
.progress-bar { padding: 16px; gap: 14px; }
|
||||
.species-hero h1 { font-size: 30px; }
|
||||
.wert { grid-template-columns: 74px 1fr auto; gap: 8px; }
|
||||
.fakten { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* ---------- Sprachumschalter ---------- */
|
||||
|
||||
.lang-switch {
|
||||
display: flex; gap: 2px; flex-shrink: 0;
|
||||
border: 1px solid var(--line); border-radius: 999px;
|
||||
background: var(--surface); padding: 2px;
|
||||
}
|
||||
.lang-switch button {
|
||||
border: 0; background: transparent; cursor: pointer;
|
||||
padding: 5px 11px; border-radius: 999px;
|
||||
font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .04em;
|
||||
color: var(--muted);
|
||||
transition: background .15s, color .15s;
|
||||
}
|
||||
.lang-switch button:hover { color: var(--text); }
|
||||
.lang-switch button.on { background: var(--accent); color: var(--accent-on); }
|
||||
|
||||
/* ---------- Filterleiste ---------- */
|
||||
|
||||
.filterleiste {
|
||||
display: flex; flex-direction: column; gap: 12px;
|
||||
padding: 14px 16px; margin-bottom: 18px;
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
border-radius: var(--r); box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.filtergruppe { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
|
||||
|
||||
.filterlabel {
|
||||
font-size: 11px; font-weight: 800; letter-spacing: .08em;
|
||||
text-transform: uppercase; color: var(--muted);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filterleiste .pills { display: flex; gap: 7px; flex-wrap: wrap; }
|
||||
.filterleiste .pills a {
|
||||
padding: 6px 13px; border-radius: 999px;
|
||||
font-size: 13px; font-weight: 650;
|
||||
border: 1px solid var(--line); background: var(--surface); color: var(--text-2);
|
||||
transition: border-color .15s, background .15s, color .15s;
|
||||
}
|
||||
.filterleiste .pills a b { font-weight: 800; opacity: .55; margin-left: 2px; }
|
||||
.filterleiste .pills a:hover { border-color: var(--muted); }
|
||||
.filterleiste .pills a.on {
|
||||
background: var(--accent); color: var(--accent-on); border-color: var(--accent);
|
||||
}
|
||||
.filterleiste .pills a.on b { opacity: .8; }
|
||||
|
||||
/* Die Verfeinerung sitzt optisch unter der Hauptachse, nicht daneben. */
|
||||
.verfeinern { border-top: 1px solid var(--line-soft, var(--line)); padding-top: 12px; }
|
||||
.verfeinern label { display: flex; align-items: center; gap: 8px; }
|
||||
|
||||
.verfeinern select {
|
||||
font: inherit; font-size: 13.5px; font-weight: 600;
|
||||
padding: 7px 30px 7px 12px; border-radius: 999px;
|
||||
border: 1px solid var(--line); background: var(--surface); color: var(--text);
|
||||
cursor: pointer;
|
||||
/* Eigener Pfeil: das native Dreieck sieht in jedem Browser anders aus. */
|
||||
-webkit-appearance: none; -moz-appearance: none; appearance: none;
|
||||
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
|
||||
linear-gradient(135deg, currentColor 50%, transparent 50%);
|
||||
background-position: calc(100% - 16px) 52%, calc(100% - 11px) 52%;
|
||||
background-size: 5px 5px, 5px 5px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.verfeinern select:hover { border-color: var(--muted); }
|
||||
.verfeinern select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
||||
|
||||
.verfeinern button {
|
||||
font: inherit; font-size: 13.5px; font-weight: 650;
|
||||
padding: 7px 16px; border-radius: 999px; cursor: pointer;
|
||||
border: 1px solid var(--accent); background: var(--accent); color: var(--accent-on);
|
||||
}
|
||||
|
||||
.zuruecksetzen {
|
||||
font-size: 13px; font-weight: 650; color: var(--muted);
|
||||
text-decoration: underline; text-underline-offset: 3px;
|
||||
}
|
||||
.zuruecksetzen:hover { color: var(--text); }
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.filterleiste { padding: 12px; gap: 10px; }
|
||||
.filtergruppe { gap: 8px; }
|
||||
/* Label über die Zeile, damit die Pillen die volle Breite bekommen. */
|
||||
.filtergruppe > .filterlabel { flex: 1 0 100%; }
|
||||
.filterleiste .pills a { padding: 6px 11px; font-size: 12.5px; }
|
||||
.verfeinern { flex-wrap: wrap; }
|
||||
.verfeinern label { flex: 1 1 46%; }
|
||||
.verfeinern label .filterlabel { display: none; }
|
||||
.verfeinern select { width: 100%; }
|
||||
}
|
||||
|
||||
/* ---------- Blickfang der Startseite ----------
|
||||
Die Seite begann mit vier Kacheln, von denen an einem ruhigen Tag zwei auf
|
||||
null standen. Jetzt steht der letzte Gast gross oben; die Zahlen sind
|
||||
dorthin gewandert, wo sie etwas erklaeren. */
|
||||
|
||||
.held {
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 24px;
|
||||
align-items: stretch; margin: 22px 0 8px;
|
||||
}
|
||||
.held-text h1 {
|
||||
font-size: 38px; line-height: 1.08; letter-spacing: -0.03em;
|
||||
font-weight: 800; margin: 0 0 10px;
|
||||
}
|
||||
.held-text p { margin: 0 0 20px; color: var(--text-2); font-size: 16px; max-width: 44ch; }
|
||||
.held .progress-bar { margin: 0; }
|
||||
|
||||
.held-bild {
|
||||
position: relative; border-radius: var(--r-lg); overflow: hidden;
|
||||
box-shadow: var(--shadow); min-height: 280px; background: var(--surface-2);
|
||||
display: block;
|
||||
}
|
||||
.held-bild img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.held-bild .schild {
|
||||
position: absolute; left: 0; right: 0; bottom: 0; padding: 40px 20px 16px;
|
||||
color: #fff;
|
||||
/* Fester Farbwert statt Token: der Verlauf liegt ueber einem Foto, nicht
|
||||
ueber dem Seitenhintergrund — er muss in beiden Modi gleich dunkeln. */
|
||||
background: linear-gradient(transparent, rgba(8,24,16,.88));
|
||||
}
|
||||
.held-bild .schild b { display: block; font-size: 22px; font-weight: 800; }
|
||||
.held-bild .schild small { opacity: .9; font-size: 13px; }
|
||||
.frisch {
|
||||
position: absolute; top: 14px; left: 14px;
|
||||
background: var(--kopf-text); color: var(--kopf-bg);
|
||||
padding: 5px 11px; border-radius: 999px;
|
||||
font-size: 11px; font-weight: 800; letter-spacing: .07em;
|
||||
}
|
||||
.held-leer {
|
||||
display: grid; place-items: center; text-align: center; padding: 30px;
|
||||
border: 1px dashed var(--line); border-radius: var(--r-lg);
|
||||
color: var(--muted); background: var(--surface);
|
||||
}
|
||||
|
||||
/* ---------- Besuche als Bildkarten ---------- */
|
||||
|
||||
.besuche { display: grid; grid-template-columns: repeat(4, 1fr); gap: 13px; }
|
||||
.besuch {
|
||||
background: var(--surface); border: 1px solid var(--line);
|
||||
border-radius: var(--r); overflow: hidden; box-shadow: var(--shadow);
|
||||
transition: transform .15s, box-shadow .15s;
|
||||
}
|
||||
.besuch:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
|
||||
/* ⚠ Feste Hoehe statt aspect-ratio: das gibt es erst ab Safari 15 und faellt
|
||||
davor ersatzlos weg, womit die Karten unterschiedlich hoch waeren. */
|
||||
.besuch img, .besuch .leer { width: 100%; height: 118px; object-fit: cover; display: block; }
|
||||
.besuch .leer { background: var(--surface-2); }
|
||||
.besuch .f { padding: 10px 12px; }
|
||||
.besuch b { font-weight: 750; font-size: 14.5px; }
|
||||
.besuch small { display: block; color: var(--muted); font-size: 12.5px; margin-top: 2px; }
|
||||
.besuch .xp { float: right; color: var(--accent); font-weight: 800; font-size: 12.5px; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.held { grid-template-columns: 1fr; }
|
||||
.held-bild { min-height: 230px; order: -1; }
|
||||
.held-text h1 { font-size: 30px; }
|
||||
.besuche { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
@media (max-width: 420px) {
|
||||
.held-text h1 { font-size: 26px; }
|
||||
.besuche { grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<!-- Vogelalbum-Marke. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<path d="M27.75 12.33 C28.34 12.31 30.07 12.24 31.17 12.33 C32.28 12.42 33.38 12.64 34.38 12.88 C35.37 13.12 36.22 13.40 37.14 13.77 C38.06 14.14 38.94 14.56 39.90 15.09 C40.86 15.63 41.93 16.29 42.88 16.97 C43.84 17.65 44.71 18.32 45.65 19.18 C46.59 20.05 47.89 21.39 48.52 22.17 C49.15 22.94 49.26 23.36 49.40 23.82 C49.55 24.28 49.48 24.60 49.40 24.93 C49.33 25.26 49.42 25.41 48.96 25.81 C48.50 26.22 47.54 26.97 46.64 27.36 C45.74 27.75 45.13 27.87 43.55 28.13 C41.96 28.39 38.56 28.63 37.14 28.91 C35.72 29.18 35.70 29.38 35.04 29.79 C34.38 30.20 33.69 30.69 33.16 31.34 C32.63 31.98 32.09 32.81 31.83 33.66 C31.58 34.50 31.56 35.61 31.61 36.42 C31.67 37.23 31.78 37.69 32.17 38.52 C32.55 39.35 33.38 40.66 33.93 41.39 C34.49 42.13 34.74 42.37 35.48 42.94 C36.22 43.51 36.99 44.21 38.35 44.82 C39.72 45.43 42.31 46.24 43.66 46.59 C45.00 46.94 45.35 46.88 46.42 46.92 C47.49 46.95 48.92 46.94 50.07 46.81 C51.21 46.68 52.41 46.38 53.27 46.14 C54.14 45.90 54.85 45.48 55.26 45.37 C55.66 45.26 56.35 44.91 55.70 45.48 C55.06 46.05 53.07 47.76 51.39 48.80 C49.72 49.83 47.34 50.97 45.65 51.67 C43.95 52.37 42.90 52.66 41.23 52.99 C39.55 53.33 37.06 53.57 35.59 53.66 C34.12 53.75 33.51 53.80 32.39 53.55 C31.26 53.29 30.20 52.85 28.85 52.11 C27.51 51.37 25.50 50.05 24.32 49.13 C23.14 48.21 22.76 47.84 21.78 46.59 C20.80 45.33 19.81 44.36 18.46 41.61 C17.12 38.87 14.78 32.52 13.71 30.12 C12.64 27.73 12.29 28.11 12.06 27.25 C11.82 26.38 12.04 25.85 12.28 24.93 C12.52 24.01 13.09 22.59 13.49 21.72 C13.90 20.86 14.23 20.40 14.71 19.73 C15.19 19.07 15.76 18.37 16.36 17.75 C16.97 17.12 17.30 16.68 18.35 15.98 C19.40 15.28 21.54 14.08 22.66 13.55 C23.79 13.01 24.27 12.96 25.09 12.77 C25.92 12.59 27.19 12.52 27.64 12.44 C28.08 12.37 27.16 12.35 27.75 12.33 Z" fill="#fcf2d3"/>
|
||||
<path d="M26.97 10.34 C27.65 10.32 29.92 10.29 30.95 10.34 C31.98 10.40 31.93 10.34 33.16 10.67 C34.39 11.01 36.99 11.80 38.35 12.33 C39.72 12.87 40.31 13.25 41.34 13.88 C42.37 14.50 43.03 14.80 44.54 16.09 C46.05 17.38 48.43 19.44 50.40 21.61 C52.37 23.79 54.43 26.46 56.36 29.13 C58.30 31.80 61.13 36.03 62.00 37.64 C62.87 39.24 62.33 37.69 61.56 38.74 C60.78 39.79 58.32 42.87 57.36 43.93 C56.40 45.00 56.71 44.74 55.81 45.15 C54.91 45.55 53.10 46.09 51.94 46.36 C50.78 46.64 49.99 46.75 48.85 46.81 C47.71 46.86 46.27 46.83 45.09 46.70 C43.92 46.57 42.87 46.35 41.78 46.03 C40.69 45.72 39.62 45.35 38.57 44.82 C37.52 44.28 36.24 43.40 35.48 42.83 C34.73 42.26 34.58 42.11 34.04 41.39 C33.51 40.67 32.66 39.35 32.28 38.52 C31.89 37.69 31.78 37.23 31.72 36.42 C31.67 35.61 31.69 34.50 31.94 33.66 C32.20 32.81 32.76 31.96 33.27 31.34 C33.79 30.71 34.39 30.29 35.04 29.90 C35.68 29.51 35.72 29.29 37.14 29.02 C38.56 28.74 41.96 28.50 43.55 28.24 C45.13 27.99 45.72 27.87 46.64 27.47 C47.56 27.06 48.59 26.24 49.07 25.81 C49.55 25.39 49.44 25.26 49.51 24.93 C49.59 24.60 49.66 24.28 49.51 23.82 C49.37 23.36 49.27 22.96 48.63 22.17 C47.99 21.37 46.60 19.96 45.65 19.07 C44.69 18.19 43.84 17.54 42.88 16.86 C41.93 16.18 40.86 15.52 39.90 14.98 C38.94 14.45 38.06 14.03 37.14 13.66 C36.22 13.29 35.37 13.01 34.38 12.77 C33.38 12.53 32.28 12.31 31.17 12.22 C30.07 12.13 28.83 12.13 27.75 12.22 C26.66 12.31 25.59 12.53 24.65 12.77 C23.71 13.01 23.03 13.23 22.11 13.66 C21.19 14.08 19.99 14.74 19.13 15.31 C18.26 15.89 17.62 16.42 16.92 17.08 C16.22 17.75 15.52 18.52 14.93 19.29 C14.34 20.07 13.82 20.86 13.38 21.72 C12.94 22.59 12.52 23.57 12.28 24.49 C12.04 25.41 11.72 26.31 11.94 27.25 C12.17 28.19 12.66 27.99 13.60 30.12 C14.54 32.26 16.44 37.58 17.58 40.07 C18.72 42.55 19.77 43.95 20.45 45.04 C21.13 46.13 20.86 45.76 21.67 46.59 C22.48 47.41 24.12 49.07 25.31 50.01 C26.51 50.95 27.82 51.67 28.85 52.22 C29.88 52.77 31.71 53.25 31.50 53.33 C31.30 53.40 28.83 53.01 27.64 52.66 C26.44 52.31 25.37 51.82 24.32 51.23 C23.27 50.64 22.18 49.83 21.34 49.13 C20.49 48.43 19.92 47.82 19.24 47.03 C18.56 46.24 17.87 45.39 17.25 44.38 C16.62 43.36 16.25 42.99 15.48 40.95 C14.71 38.91 13.27 33.95 12.61 32.11 C11.94 30.27 12.04 30.64 11.50 29.90 C10.97 29.16 9.97 28.22 9.40 27.69 C8.83 27.16 9.31 27.40 8.08 26.70 C6.84 26.00 2.44 24.23 2.00 23.49 C1.56 22.76 3.80 22.70 5.43 22.28 C7.05 21.85 10.31 21.76 11.72 20.95 C13.14 20.14 12.96 18.59 13.93 17.41 C14.91 16.24 16.70 14.65 17.58 13.88 C18.46 13.10 18.39 13.22 19.24 12.77 C20.08 12.33 21.39 11.61 22.66 11.23 C23.93 10.84 26.14 10.60 26.86 10.45 C27.58 10.31 26.29 10.36 26.97 10.34 Z" fill="#106943"/>
|
||||
<path d="M39.13 29.02 C39.81 29.02 41.17 28.74 43.10 29.13 C45.04 29.51 48.72 30.86 50.73 31.34 C52.74 31.82 54.43 31.82 55.15 32.00 C55.87 32.18 55.39 32.06 55.04 32.44 C54.69 32.83 53.73 33.80 53.05 34.32 C52.37 34.84 51.80 35.15 50.95 35.54 C50.10 35.92 48.30 36.35 47.97 36.64 C47.64 36.94 48.19 36.99 48.96 37.30 C49.73 37.62 51.47 38.21 52.61 38.52 C53.75 38.83 54.84 39.03 55.81 39.18 C56.79 39.33 58.02 39.33 58.46 39.40 C58.91 39.48 58.78 39.35 58.46 39.62 C58.15 39.90 57.32 40.60 56.59 41.06 C55.85 41.52 54.89 42.02 54.04 42.39 C53.20 42.76 52.39 43.03 51.50 43.27 C50.62 43.51 49.79 43.71 48.74 43.82 C47.69 43.93 46.13 43.95 45.20 43.93 C44.28 43.92 43.99 43.86 43.22 43.71 C42.44 43.57 41.43 43.34 40.56 43.05 C39.70 42.76 38.91 42.46 38.02 41.94 C37.14 41.43 35.94 40.55 35.26 39.96 C34.58 39.37 34.34 39.03 33.93 38.41 C33.53 37.78 33.05 36.94 32.83 36.20 C32.61 35.46 32.53 34.67 32.61 33.99 C32.68 33.31 32.92 32.68 33.27 32.11 C33.62 31.54 34.15 30.99 34.71 30.56 C35.26 30.14 35.87 29.81 36.59 29.57 C37.30 29.33 38.59 29.22 39.02 29.13 C39.44 29.03 38.45 29.02 39.13 29.02 Z" fill="#fcf2d3"/>
|
||||
<path d="M26.86 17.97 C27.25 17.95 28.54 17.91 29.07 17.97 C29.61 18.02 29.66 18.10 30.07 18.30 C30.47 18.50 31.02 18.74 31.50 19.18 C31.98 19.62 32.61 20.38 32.94 20.95 C33.27 21.52 33.40 22.02 33.49 22.61 C33.58 23.20 33.55 23.97 33.49 24.49 C33.44 25.00 33.36 25.24 33.16 25.70 C32.96 26.16 32.79 26.71 32.28 27.25 C31.76 27.78 30.62 28.57 30.07 28.91 C29.51 29.24 29.46 29.18 28.96 29.24 C28.46 29.29 27.75 29.35 27.08 29.24 C26.42 29.13 25.70 29.05 24.98 28.57 C24.27 28.10 23.25 27.06 22.77 26.36 C22.29 25.66 22.20 24.76 22.11 24.38 C22.02 23.99 22.17 23.90 22.22 24.04 C22.28 24.19 22.17 24.67 22.44 25.26 C22.72 25.85 23.27 26.97 23.88 27.58 C24.49 28.19 25.59 28.70 26.09 28.91 C26.59 29.11 26.99 29.00 26.86 28.80 C26.73 28.59 25.74 28.08 25.31 27.69 C24.89 27.30 24.60 26.97 24.32 26.48 C24.04 25.98 23.77 25.33 23.66 24.71 C23.55 24.08 23.47 23.47 23.66 22.72 C23.84 21.96 24.41 20.77 24.76 20.18 C25.11 19.59 25.37 19.48 25.76 19.18 C26.14 18.89 26.90 18.57 27.08 18.41 C27.27 18.24 27.03 18.21 26.86 18.19 C26.70 18.17 26.48 18.15 26.09 18.30 C25.70 18.45 25.02 18.72 24.54 19.07 C24.06 19.42 23.53 20.01 23.22 20.40 C22.90 20.78 22.70 21.37 22.66 21.39 C22.63 21.41 22.79 20.84 22.99 20.51 C23.20 20.18 23.42 19.77 23.88 19.40 C24.34 19.03 25.28 18.52 25.76 18.30 C26.24 18.08 26.57 18.13 26.75 18.08 C26.94 18.02 26.48 17.99 26.86 17.97 Z" fill="#106943"/>
|
||||
<path d="M26.09 18.52 C26.18 18.50 26.75 18.28 26.53 18.52 C26.31 18.76 25.19 19.51 24.76 19.96 C24.34 20.40 24.21 20.67 23.99 21.17 C23.77 21.67 23.53 22.39 23.44 22.94 C23.34 23.49 23.25 23.80 23.44 24.49 C23.62 25.17 24.08 26.35 24.54 27.03 C25.00 27.71 26.16 28.41 26.20 28.57 C26.24 28.74 25.20 28.30 24.76 28.02 C24.32 27.75 23.93 27.51 23.55 26.92 C23.16 26.33 22.63 25.17 22.44 24.49 C22.26 23.80 22.35 23.40 22.44 22.83 C22.53 22.26 22.64 21.65 22.99 21.06 C23.34 20.47 24.04 19.70 24.54 19.29 C25.04 18.89 25.72 18.76 25.98 18.63 C26.24 18.50 26.00 18.54 26.09 18.52 Z" fill="#fefefe"/>
|
||||
<path d="M26.86 19.85 C26.99 19.85 27.27 19.83 27.52 19.96 C27.78 20.08 28.22 20.43 28.41 20.62 C28.59 20.80 28.63 20.77 28.63 21.06 C28.63 21.36 28.63 22.04 28.41 22.39 C28.19 22.74 27.65 23.05 27.30 23.16 C26.95 23.27 26.60 23.18 26.31 23.05 C26.01 22.92 25.68 22.74 25.54 22.39 C25.39 22.04 25.33 21.34 25.43 20.95 C25.52 20.56 25.87 20.23 26.09 20.07 C26.31 19.90 26.62 19.99 26.75 19.96 C26.88 19.92 26.73 19.85 26.86 19.85 Z" fill="#fefefe"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "Vogelalbum",
|
||||
"short_name": "Vogelalbum",
|
||||
"description": "Welche Vögel besuchen das Vogelhaus? Erkannt, gesammelt, katalogisiert.",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#106943",
|
||||
"icons": [
|
||||
{ "src": "/static/img/symbol-192.png", "sizes": "192x192", "type": "image/png" },
|
||||
{ "src": "/static/img/symbol-512.png", "sizes": "512x512", "type": "image/png" },
|
||||
{ "src": "/static/img/symbol-512.png", "sizes": "512x512", "type": "image/png",
|
||||
"purpose": "maskable" }
|
||||
]
|
||||
}
|
||||